from PIL import Image from database import * import base64 import io import aiohttp from kutana import Plugin from utils.static_text import need_vip from utils import priviligeshelper plugin = Plugin(name="Жмыхает фоточки", cmds=[{ 'command': 'жмых [фото]', 'desc': 'делает упоротую фотку', 'vip': True }]) ACCESS_TOKEN = "my-api-token he he boi" @plugin.on_startswith_text("жмых") async def on_message(message, attachments, env): privs = await priviligeshelper.getUserPriviliges(env, message.from_id) if not (privs & priviligeshelper.USER_VIP > 0): return await env.reply(need_vip) photo = False for x in attachments: if x.type == "photo": photo = True break if not photo:
from kutana import Plugin import peewee_async, peewee, datetime, time, asyncio from kutana.database import * import traceback from kutana.vksm import * plugin = Plugin(category="Статистика") plugin.desc = {'юзер стат': ['(имя)', 'считaет вcю cтaтистику пользователя - количествo соoбщeний, символов, матов и его рейтинг в топе'], 'стата чата': [0, 'аналогично команде юзер стат, только для чата'], 'топ бесед': [0, 'пoказывaет топ бeсeд (по cообщениям), в которых пpисутствует бoт'], 'актив': [0, 'пoказываeт ктo и кoгдa поcлeдний pаз что-тo пиcaл в чатe']} plugin.category_desc = 'Считaeт всю статистику бeceды - кoличеcтво cоoбщений, символов, cтикeров, прикреплeний, голocoвыx cooбщeний, иcпoльзованных кoманд - кaк для пoльзoвaтеля, тaк и для всeгo чата, и пoкaзывает эту информацию' class chat_stats_vlad(BaseModel): chat_id = peewee.IntegerField(default=0) messages = peewee.BigIntegerField(default=0) clear_messages = peewee.BigIntegerField(default=0) clear_symbols = peewee.BigIntegerField(default=0) symbols = peewee.BigIntegerField(default=0) voice_messages = peewee.BigIntegerField(default=0) resend_messages = peewee.BigIntegerField(default=0) photos = peewee.BigIntegerField(default=0) videos = peewee.BigIntegerField(default=0) audios = peewee.BigIntegerField(default=0) docs = peewee.BigIntegerField(default=0) posts = peewee.BigIntegerField(default=0) stickers = peewee.BigIntegerField(default=0) mentios = peewee.BigIntegerField(default=0) links = peewee.BigIntegerField(default=0) leaved = peewee.BigIntegerField(default=0) messages_with_sw = peewee.BigIntegerField(default=0) last_user_id = peewee.IntegerField(default=0)
from kutana import Plugin import aiohttp, json from kutana.vksm import * plugin = Plugin() async def get_rate(to): async with aiohttp.ClientSession() as sess: async with sess.get(f"https://www.cbr-xml-daily.ru/daily_json.js") as resp: try: data = await resp.text() res = json.loads(data) return res['Valute'][to]['Value'] except (KeyError, IndexError): raise ValueError('Курса данной валюты не найдено') async def get_btc(): async with aiohttp.ClientSession() as sess: async with sess.get(f"https://blockchain.info/ru/ticker") as resp: res = await resp.json() return toFixed(res['RUB']['sell'], 2), toFixed(res['USD']['sell'], 2) def toFixed(f: float, n=0): a, b = str(f).split('.') return '{}.{}{}'.format(a, b[:n], '0'*(n-len(b))) @plugin.on_startswith_text("курс") async def course(message, attachments, env): data = [] for cur in ('USD', 'EUR'): data.append(await get_rate(cur)) usd, eur = data
from kutana import Plugin from peewee_async import Manager from bot.db import User from bot.roles import owner_global_role, UserRoles from bot.utils import extract_users plugin = Plugin('Add developers[develop]') async def make_dev(mgr: Manager, users): added = [] async with mgr.atomic(): for user_id in users: user, created = await mgr.get_or_create(User, id=user_id) if user.role < UserRoles.DEVELOPER.value: user.role = UserRoles.DEVELOPER.value await mgr.update(user) added.append(user_id) return added async def del_dev(mgr: Manager, users): deleted = [] for user_id in users: user, created = await mgr.get_or_create(User, id=user_id) if user.role > UserRoles.USER.value: user.role = UserRoles.USER.value await mgr.update(user) deleted.append(user_id) return deleted
from kutana import Plugin import random plugin = Plugin(name="Рандом", cmds=[{ 'command': 'рандом <от> <до>', 'desc': 'случайное число в диапазоне ОТ ДО' }]) @plugin.on_startswith_text("рандом") async def on_message(message, attachments, env): try: args = [int(arg) for arg in env['args']] except ValueError: return await env.reply("Один из аргументов - не число") # Если у нас два аргумента - это диапазон if len(args) == 2: start, end = args # Конечное значение больше начального if abs(end - start) > 0: num = random.randint(start, end) # Конечное число меньше начального else: num = random.randint(end, start) # Если один аргумент, то диапазон будет - (1, число) elif len(args) == 1: num = random.randint(1, args[0])
from kutana import Plugin import random plugin = Plugin(name="Попытаемся сделать музочку?", cmds=[{ 'command': 'randomsong <N>', 'desc': 'Получает <N> количество рандомных песен' }]) def get_n_songs(count): audios = [] i = 0 while (i < count): server_id = random.randint(1, 291461) audio_id = random.randint(456239017, 456239700) audios.append(f"audio2000{server_id}_{audio_id},") i += 1 return ''.join(audios) @plugin.on_startswith_text("randomsong") async def random_song(message, attachments, env): if not env['args']: return await env.reply("<N> количество - от 1 до 10") if not env['args'][0].isdigit() or int(env['args'][0]) > 10: return await env.reply("<N> количество - от 1 до 10") att_songs = get_n_songs(int(env['args'][0]))
from kutana import Plugin from kutana.structures import objdict from utils import ddict, edict, parse_user_name import asyncio from operator import is_not from functools import partial plugin = Plugin(name="Статистика чата", cmds=[{'command': 'chatstats', 'desc': 'отображение статистики текущего чата.'}, {'command': 'raiting', 'desc': 'отображение топа из чатов!'}], order=30) async def getname(env, user_id): users = {} for u in env.eenv.meta_data.users: if 'name' in u: continue users[u["id"]] = u['first_name'] + " " + u["last_name"] resuser = users.get(user_id) if not resuser: if user_id < 0: return user_id us = await env.request('users.get', user_ids=user_id, fields="sex,screen_name,nickname") if not us: return user_id name1 = us.response[0]["first_name"] + " " + us[0]["last_name"] return name1 return resuser async def getrawname(env, user_id): us = await env.request('users.get', user_ids=user_id, fields="sex,screen_name,nickname")
def test_happy_path(mock_post): group_change_settings_update = { "type": "group_change_settings", "object": { "changes": { "screen_name": { "old_value": "", "new_value": "sdffff23f23" }, "title": { "old_value": "Спасибо", "new_value": "Спасибо 2" } } } } raw_updates = [ {}, { "type": "present" }, group_change_settings_update, MESSAGES["not_message"], MESSAGES["message"], MESSAGES[".echo"], MESSAGES[".echo chat"], MESSAGES[".echo wa"], ] answers = [] updated_longpoll = [] def acquire_updates(content_type=None): if not raw_updates: return {"updates": [], "ts": "100"} if updated_longpoll == [1]: return {"failed": 3} return {"updates": [raw_updates.pop(0)], "ts": "0"} mock_post.return_value.__aenter__.return_value.json = CoroutineMock( side_effect=acquire_updates) class _VkontakteLongpoll(VkontakteLongpoll): async def _get_response(self, method, kwargs={}): if method == "groups.setLongPollSettings": return {"response": 1} if method == "groups.getById": return { "response": [ { "id": 1, "name": "group", "screen_name": "grp" }, ], } if method == "groups.getLongPollServer": updated_longpoll.append(1) return { "response": { "server": "s", "key": "k", "ts": "1", }, } if method == "execute": answers.extend(kwargs["code"].split("API.")[1:]) return { "response": [1] * kwargs["code"].count("API."), } print(method, kwargs) app = Kutana() vkontakte = _VkontakteLongpoll(token="token") app.add_backend(vkontakte) echo_plugin = Plugin("echo") @echo_plugin.on_commands(["echo", "ec"]) async def _(message, ctx): assert ctx.resolve_screen_name assert ctx.reply await ctx.reply(message.text, attachments=message.attachments) app.add_plugin(echo_plugin) app.get_loop().call_later( delay=vkontakte.api_request_pause * 4, callback=app.stop, ) app.run() assert vkontakte.group_name == "Спасибо 2" assert vkontakte.group_screen_name == "sdffff23f23" assert len(updated_longpoll) == 2 answers.sort() assert len(answers) == 3 assert '{"message": ".echo chat [michaelkrukov|Михаил]",' in answers[0] assert '{"message": ".echo wa",' in answers[1] assert 'attachment": ""' not in answers[1] assert '{"message": ".echo",' in answers[2]
from kutana import Plugin, HandlerResponse # Plugin's global variable statistics = {} # Sub-plugin to collect statistics plugin = Plugin( name="Statistics", description="Show word count in current dialog", ) @plugin.on_messages(priority=5) async def __(msg, ctx): words_count = statistics.get(msg.sender_id, 0) new_words_count = words_count + len(msg.text.split()) statistics[msg.sender_id] = new_words_count return HandlerResponse.SKIPPED @plugin.on_commands(["statistics"]) async def __(msg, ctx): await ctx.reply("You wrote: {} words.".format( statistics.get(msg.sender_id, 0)))
from kutana import Plugin, MemoryStorage plugin = Plugin("Give image") # Constants WAITING_STATE = "give_image:waiting" # Handlers @plugin.on_commands(["give_image", "giveimage"]) @plugin.expect_sender(state="") async def __(msg, ctx): if msg.attachments: await ctx.reply("You image:", attachments=msg.attachments[0]) return await ctx.sender.update({"state": WAITING_STATE}) await ctx.reply("Please, send you image") @plugin.on_attachments(["image"]) @plugin.expect_sender(state=WAITING_STATE) async def __(msg, ctx): await ctx.sender.update({"state": ""}) await ctx.reply("You image:", attachments=msg.attachments[0]) @plugin.on_unprocessed_messages() @plugin.expect_sender(state=WAITING_STATE) async def __(msg, ctx): await ctx.sender.update({"state": ""})
from kutana import Plugin from database import * from PIL import Image from PIL import ImageDraw from PIL import ImageFont from PIL import ImageOps from utils import priviligeshelper from utils.static_text import need_vip import io import aiohttp plugin = Plugin(name="Этот пользователь!", cmds=[{'command': 'этот пользователь <текст>', 'desc': 'делает карточку "этот пользователь"', 'vip': True}]) PATH = "plugins/this_user/" textd = "Этот пользователь " sizes = (668, 189) # In-card space sizes limit = 27 # Limit an symbols in one line limit_lines = 4 # Limit lines! fd = ImageFont.truetype(PATH + "font.ttf", 30) color_font = (242, 174, 127) def chuncked_text(l, n): for i in range(0, len(l), n + 2): yield l[i:i + n + 2] @plugin.on_startswith_text("этот пользователь")
from kutana import Plugin, HandlerResponse # Plugins for demonstrating how contexts works plugin1 = Plugin(name="Contexts [Provider]") @plugin1.on_commands(["contexts"], priority=5) async def _(msg, ctx): # Do or produce something and save it to context. ctx.var = "val" # Let other plugins work return HandlerResponse.SKIPPED plugin2 = Plugin(name="Contexts") @plugin2.on_commands(["contexts"]) async def _(msg, ctx): await ctx.reply('ctx.var == "{}"'.format( ctx.var # Use value saved in context by other plugin )) plugins = [plugin1, plugin2] # Order matters
from kutana import Plugin, Attachment, HandlerResponse, get_path import os import re plugin = Plugin(name="Music", description="Send music") @plugin.on_commands(["m"]) async def _(msg, ctx): files = files_find("/home/hord/Музыка/", ctx.body) if (len(files) == 0): await ctx.reply("Нет такой музыки...") else: filepath = files.pop() with open(get_path(__file__, filepath), "rb") as fh: audio_message = Attachment.new(fh.read(), os.path.basename(filepath), "voice") await ctx.reply(os.path.basename(filepath), attachments=audio_message) def files_find(catalog, f): find_files = [] for root, dirs, files in os.walk(catalog): find_files += [ os.path.join(root, name) for name in files if re.search(f, name, re.IGNORECASE) ]
from kutana import Plugin from database import * from utils import check_admin, ddict, edict, parse_user_id, plural_form, priviligeshelper import time import datetime plugin = Plugin( name="Активы чата", cmds=[{ 'command': 'актив <кол-во дней>', 'desc': 'Показывает активных людей в чате за определённый период времени!' }, { 'command': 'кик <имя>', 'desc': 'кикает человека с чата' }]) @plugin.on_startswith_text("актив") async def active_chat(message, attachments, env): if not env.eenv.is_multichat or not env.eenv.meta_data: return await env.reply( "Эту команду надо использовать в беседе и бот должен быть администратором!" ) data_users = await ddict(await env.eenv.dbredis.get( f"honoka:active_users_multichat:{message.peer_id}")) if not data_users or data_users and len(data_users['users']) < 1: return await env.reply("Пока у меня нету данных для тебя.")
from kutana import Plugin import romajitable import transliterate plugin = Plugin(name="Кана", cmds=[{ 'command': 'jp <текст>', 'desc': 'переводит текст на япу(фан)' }]) @plugin.on_startswith_text("jp") async def on_message(message, attachments, env): if not env['args']: return await env.reply("Введите текст пожалуйста") text = ' '.join(env['args']) try: text = transliterate.translit(text, reversed=True) except Exception: pass rt = romajitable.to_kana(text) return await env.reply("А вот и результат подъехал\n\n" f"🔑🇯🇵|Хирагана: {rt.hiragana}\n" f"🔑🇯🇵|Катакана: {rt.katakana}")
from kutana import Plugin plugin = Plugin("echo") @plugin.on_commands(["echo", "ec"]) async def _(message, ctx): await ctx.reply(ctx.route["args"])
{ "action": { "type": "text", "payload": "4", "label": "Four" }, "color": "primary", }, ]], } # Keyboard that will be send to VKONTAKTE is a STRING! KEYBOARD_STRING = json.dumps(KEYBOARD_OBJECT) # Plugins for sending keyboard. plugin1 = Plugin(name="Keyboard", description="Keyboard for vkontakte") @plugin1.on_text("keyboard") async def _(message, env): if env.manager_type != "vkontakte": await env.reply("This example works only for vk.com") return await env.reply("Keyboard", keyboard=KEYBOARD_STRING) # Plugin for intercepting messages with payload. plugin2 = Plugin(name="_Keyboard_listener", priority=10)
from kutana import Plugin plugin = Plugin(name="Plugins") plugins = [] @plugin.on_startup() async def startup(app): for pl in app.registered_plugins: if isinstance(pl, Plugin) and pl.name[:1] != "_": plugins.append(pl.name) @plugin.on_text("list") async def on_list(message, env): await env.reply( "Plugins:\n" + "; ".join(plugins) )
from kutana import Plugin plugin = Plugin(name="Stop", description="Turns application off") @plugin.on_start() async def __(app): plugin.stop = app.stop @plugin.on_commands(["stop"]) async def __(msg, ctx): plugin.stop()
from kutana import Plugin, Message plugin = Plugin(name="Prefix", priority=5) # default priority is 0 PREFIXES = (".", "/") @plugin.on_has_text() async def on_has_text(message, env): for prefix in PREFIXES: if message.text[:len(prefix)] == prefix: break else: return "DONE" env.parent.set_message( Message(message.text[len(prefix):], message.attachments, message.from_id, message.peer_id, message.date, message.raw_update)) return "GOON"
from kutana import Plugin import random from random import sample from database import * from utils import priviligeshelper plugin = Plugin(name="Кто? Кто в кого влюблён", cmds=[{ 'command': 'кто <определение>', 'desc': 'кто в конференции является обладателем определения.' }, { 'command': 'кто кого', 'desc': 'кто кого же любит в беседе? Хмммммм' }, { 'command': 'ктогей', 'desc': 'поиск петушков' }]) @plugin.on_startswith_text("кто кого", "ктокого") async def on_message(message, attachments, env): if env.eenv.is_multichat and env.eenv.meta_data: love1, love2 = sample(env.eenv.meta_data.users, 2) await env.reply( f"[id{love1['id']}|{love1['first_name']} {love1['last_name']}] - ❤ Любит ❤ - [id{love2['id']}|{love2['first_name']} {love2['last_name']}]" ) else: await env.reply( "Эту команду можно использовать только в беседе, и при условии что у бота есть права администратора."
from kutana import Plugin # Plugin's global variable statistics = {} # Sub-plugin to collect statistics p1 = Plugin(name="_Statistics collector", priority=10) @p1.on_has_text() async def _(message, env): words_count = statistics.get(message.from_id, 0) new_words_count = words_count + len(message.text.split()) statistics[message.from_id] = new_words_count return "GOON" # Sub-plugin to show statistics p2 = Plugin(name="Statistics", description="Show word count in current dialog") @p2.on_text("statistics") async def _(message, env): await env.reply("You wrote: {} words.".format( statistics.get(message.from_id, 0))) # List of plugins to export
import random from utils import parse_user_id, VKKeyboard, get_nekos_attach plugin = Plugin(name="Отношения", cmds=[{ 'command': 'отношения встречаться <id>', 'desc': 'начать встречаться с <id>' }, { 'command': 'отношения поцеловать', 'desc': 'поцеловать свою девушку/парня' }, { 'command': 'отношения обнять', 'desc': 'обнять свою девушку/парня' }, { 'command': 'отношения погладить', 'desc': 'погладить свою девушку/парня' }, { 'command': 'отношения порвать', 'desc': 'порвать с отношениями' }, { 'command': 'отношения заняться сексом', 'desc': 'ну тут я думаю вы всё поняли ;D' }, { 'command': 'отношения шлёпнуть', 'desc': 'шлёпнуть своего партнёра по попе ;D' }, { 'command': 'отношения инфа', 'desc': 'информация об отношениях' }]) relationtemp = {}
from kutana import Plugin import psutil import time import os plugin = Plugin(name="Metrics", description="Send some information") @plugin.on_commands(["metrics"]) async def __(msg, ctx): process = psutil.Process(os.getpid()) taken_memory = int(process.memory_info().rss / 2**20) taken_time = time.time() - msg.date await ctx.reply("mem: ~{}mib; tim: {}s".format(taken_memory, taken_time))
import pytest from kutana import ( Plugin, Message, Update, UpdateType, Attachment, HandlerResponse as hr, ) from testing_tools import make_kutana_no_run # --------------------------------------- QUEST pl = Plugin("Quest") @pl.on_commands(["start"], user_state="") async def _(msg, ctx): await ctx.set_state(user_state="quest:1") await ctx.reply("Choose: left or right") @pl.on_commands(["left"], user_state="quest:1") async def _(msg, ctx): await ctx.set_state(user_state="quest:end") await ctx.reply("You have chosen: left\nWrite '.ok'") @pl.on_commands(["right"], user_state="quest:1") async def _(msg, ctx): await ctx.set_state(user_state="quest:end") await ctx.reply("You have chosen: right\nWrite '.ok'")
from kutana import Plugin plugin = Plugin(name="Echo") @plugin.on_startswith_text("echo") async def on_echo(message, env): await env.reply("{}".format(env.body), attachment=message.attachments)
from utils.static_text import need_vip from utils import VKKeyboard import utils.logs as Logs plugin = Plugin(name="Economy-Games", cmds=[{ 'command': 'double [r|g|b] [ставка]', 'desc': 'ну тип рулетка' }, { 'command': 'казино [ставка]', 'desc': 'сыграем?' }, { 'command': 'бин [вверх/вниз] [ставка]', 'desc': 'бинарные опционы' }, { 'command': 'ловушка [ставка]', 'desc': 'попробуй достать сокровища!' }, { 'command': 'эко бонус', 'desc': 'получитс бонус от бота', 'vip': True }, { 'command': 'игра [ставка]', 'desc': 'просто игрулька)' }, { 'command': 'дснять', 'desc': 'снять мани, за то что писал!' }]) # 31give # 32cazino
from kutana import Plugin, Message plugin = Plugin(name="Система префиксов!", priority=500) @plugin.on_has_text() async def on_has_text(message, attachments, env): if not message.text.startswith(env.eenv.prefix): return "DONE" # "GOON" if you want to just keep message env.eenv['prefixes'] = env.eenv.prefix env.eenv._cached_message = Message(message.text[len(env.eenv.prefix):], message.attachments, message.from_id, message.peer_id, message.raw_update) return "GOON"
import os import sys import time from kutana import Plugin, Context from bot.roles import developer_global_role from bot.utils import get_users plugin = Plugin('Manage process', '(re)start bot') @plugin.on_start() async def _(app): if len(sys.argv) == 5 and sys.argv[1] == '--restarted': for backend in app.get_backends(): if backend.get_identity() == 'vkontakte': elapsed_time = time.time() - float(sys.argv[2]) chat_id = int(sys.argv[3]) user_id = int(sys.argv[4]) user = (await get_users(backend, user_id, 'ins'))[0] user_name = user['first_name'] + ' ' + user['last_name'] await backend.send_message( chat_id, f"Бот был перезапущен [id{user_id}|{user_name}] за {elapsed_time:.2f} сек", disable_mentions=1) @plugin.on_commands(['рестарт', 'restart']) @developer_global_role async def restart_command(msg, ctx: Context):
def test_vk_full(self): plugin = Plugin() self.called = False self.called_on_raw = False self.called_on_attachment = False async def on_attachment(message, attachments, env): self.called_on_attachment = True return "GOON" plugin.on_attachment("photo")(on_attachment) async def on_regexp(message, attachments, env): # Test receiving self.assertEqual(env.match.group(1), "message") self.assertEqual(env.match.group(0), "echo message") self.assertEqual(message.attachments, attachments) self.assertEqual(len(attachments), 2) self.assertTrue(attachments[0].link) self.assertTrue(attachments[1].link) # Test sending a_image = await env.upload_photo("test/test_assets/author.png") a_image = await env.upload_photo("test/test_assets/author.png", peer_id=False) a_audio = await env.upload_doc("test/test_assets/girl.ogg", doctype="audio_message", filename="file.ogg") self.assertTrue(a_image.id) self.assertTrue(a_audio.id) resps = await env.reply("Спасибо.", attachment=a_image) self.assertTrue(resps[0].response) for resp in resps: resp = await env.request("messages.delete", message_ids=str(resp.response), delete_for_all=1) self.assertTrue(resp.response) # Test failed request resp = await env.request("messages.send") self.assertTrue(resp.error) self.assertTrue(resp.errors[0][1]) self.assertEqual(resp.errors[0][0], "VK_req") self.assertFalse(resp.response) self.called = True plugin.on_regexp_text(r"echo (.+)")(on_regexp) async def on_raw(update, env): self.called_on_raw = True return "GOON" plugin.on_raw()(on_raw) self.kutana.executor.register_plugins(plugin) self.kutana.run() self.assertTrue(self.called) self.assertTrue(self.called_on_raw) self.assertTrue(self.called_on_attachment)