import asyncio import os import re from aiotg import Bot, Chat from collections import defaultdict, deque from pprint import pprint from datetime import datetime, timedelta bot = Bot(api_token=os.environ['API_KEY']) last_msgs = defaultdict(lambda: deque(maxlen=10)) def find_original(message): if 'text' in message: return message['text'] elif 'caption' in message: return message['caption'] return None async def doit(chat, match): date = datetime.fromtimestamp(chat.message['date']) if date < datetime.now() - timedelta(minutes=5): print('ignoring old message from', date) return fr = match.group(1) to = match.group(2) to = (to.replace('\\/', '/').replace('\\0', '\\g<0>')) try:
import pytest import random from aiotg import Bot, Chat, InlineQuery from aiotg import MESSAGE_TYPES, MESSAGE_UPDATES from aiotg.mock import MockBot from testfixtures import LogCapture API_TOKEN = "test_token" bot = Bot(API_TOKEN) def custom_msg(msg): template = { "message_id": 0, "from": { "first_name": "John" }, "chat": { "id": 0, "type": "private" } } template.update(msg) return template def text_msg(text): return custom_msg({"text": text})
import os import asyncio from aiotg import Bot bot = Bot(os.environ["API_TOKEN"]) channel = bot.channel(os.environ["CHANNEL"]) private = bot.private(os.environ["PRIVATE"]) async def greeter(): await channel.send_text("Hello from channel!") await private.send_text("Why not say hello directly?") loop = asyncio.get_event_loop() loop.run_until_complete(greeter())
import os import aiohttp from aiotg import Bot bot = Bot(os.environ["API_TOKEN"]) @bot.command(r"bitcoin") async def bitcoin(chat, match): url = "https://api.bitcoinaverage.com/ticker/global/USD/" async with aiohttp.get(url) as s: info = await s.json() await chat.send_text(info["24h_avg"]) if __name__ == '__main__': bot.run()
from uuid import uuid4 from aiotg import Bot, Chat from asyncio import Queue from alerter.config import TELEGRAM_TOKEN from alerter.Subscribers import Subscriber from alerter.BaseClasses import Manager PIN_TYPE = "telegram" HELP_MSG = """/subscriptions - show you current subscriptions (could you unsubscribe) /subscribe [regex] - subscribe on all notifications or part, examples: /subscribe, /subscribe _uat, /subscribe RC180312 """ assert TELEGRAM_TOKEN bot = Bot(api_token=TELEGRAM_TOKEN) def inl_button(text, callback_name): return { 'type': 'InlineKeyboardButton', 'text': text, 'callback_data': '%s' % (callback_name), } def get_subscribe_markup(source_types, callback_name): return { 'type': 'ReplyKeyboardMarkup', 'inline_keyboard': [ [inl_button(source_type, '%s-%s' % (callback_name, source_type))]
import datetime import os from aiotg import Bot, Chat bot = Bot(api_token=os.getenv('TOKEN', '')) base_phrase = "Do you ask me about deploy to prod?\n" @bot.command(r".*") def echo(chat: Chat, match): date = datetime.date.today() day_x = datetime.date(year=2019, month=3, day=14,) if date == day_x: return chat.reply("NO, YOU CAN'T TODAY!") else: return chat.reply("Yes, ofcourse, if tests are good and you want to.") bot.run()
class PytrackTelegramBot(object): def __init__(self): self.connection = Connection(YOUTRACK_BASE_URL, login=YOUTRACK_LOGIN, password=YOUTRACK_PASSWORD) self.bot = Bot(TELEGRAM_API_KEY) self.db_pool = None self.update_timer = None self.logger = logging.getLogger('TG-bot') async def init(self): """ Initializes app """ self.logger.info("Initializing") self.db_pool = await db.create_pool(POSTGRESQL_DSN) projects = await self.connection.get_projects() _users = await self.connection.get_users() users = [] for _user in _users: user = await self.connection.get_user(_user['login']) users.append((user['login'], user['fullName'])) async with self.db_pool.acquire() as conn: await db.ensure_projects_are_present(conn, projects) await db.ensure_users_are_present(conn, users) async def run(self): #self.logger.info("Starting timer") #self.update_timer = aiotools.create_timer( # self.check_for_updates, 300.0) await self.check_for_updates(30) async def shutdown(self): self.logger.info("Shutting down") del self.bot await self.db_pool.close() if self.update_timer: self.update_timer.cancel() await self.update_timer def create_mention(self, user): if user['tg_id']: return "[%s](tg://user?id=%s)" % (user['full_name'], user['tg_id']) else: return "@%s" % user['youtrack_id'] def create_issue_link(self, issue_id): return "[{0}]({1}/issue/{0})".format(issue_id, YOUTRACK_BASE_URL) def render_message(self, mention, comment): return MESSAGE_TEMPLATE % (mention, self.create_issue_link( comment['issueId']), comment['text']) def render_change_message(self, mention, issue, change): updated_tmpl = "- {0}: {1} -> {2}" updates = (updated_tmpl.format( field.name, field.old_value[0] if field.old_value else "n/a", field.new_value[0] if field.new_value else "n/a") for field in change.fields) updates = "\n".join(updates) return MESSAGE_CHANGE_TEMPLATE % ( mention, self.create_issue_link(issue['id']), updates) async def try_post_markdown(self, chat, message): try: await chat.send_text(message, parse_mode='Markdown') except BotApiError: self.logger.warning( "Cannot send message '%s' as markdown, resending as text", message) await chat.send_text(message) async def post_comment(self, comment): async with self.db_pool.acquire() as conn: project_id = comment['issueId'].split('-')[0] chat_id = await db.get_project_chat_id(conn, project_id) user = await db.get_user(conn, comment['author']) mention = self.create_mention(user) message = self.render_message(mention, comment) self.logger.info("Posting comment %s to chat %s", comment['id'], chat_id) chat = self.bot.channel(chat_id) await self.try_post_markdown(chat, message) await db.set_comment_posted(conn, comment) async def post_change(self, issue, change): async with self.db_pool.acquire() as conn: project_id = issue['id'].split('-')[0] chat_id = await db.get_project_chat_id(conn, project_id) user = await db.get_user(conn, change.updater_name) mention = self.create_mention(user) message = self.render_change_message(mention, issue, change) self.logger.info("Posting issue %s change to chat %s", issue['id'], chat_id) chat = self.bot.channel(chat_id) await self.try_post_markdown(chat, message) async def post_new_issue(self, issue): async with self.db_pool.acquire() as conn: project_id = issue['id'].split('-')[0] user = await db.get_user(conn, issue['reporterName']) chat_id = await db.get_project_chat_id(conn, project_id) mention = self.create_mention(user) issue_link = self.create_issue_link(issue['id']) summary = issue['summary'] issue_type = issue['Type'] assignee = issue.get('Assignee', None) assignee_mention = None if assignee: try: assignee_mention = self.create_mention(await db.get_user( conn, assignee)) except: self.logger.warning("Could not create assignee mention") if assignee_mention: message = f"{mention} создал задачу {issue_link}: {summary} с типом {issue_type}. Задача назначена на {assignee_mention}." else: message = f"{mention} создал задачу {issue_link}: {summary} с типом {issue_type}." chat = self.bot.channel(chat_id) await self.try_post_markdown(chat, message) async def check_issue(self, issue, last_updated): self.logger.info("Checking issue %s", issue['id']) last_checked = 0 project_id = issue['id'].split('-')[0] async with self.db_pool.acquire() as conn: created = int(issue['created']) if created > last_updated: # new issue last_checked = max(last_checked, created) await self.post_new_issue(issue) if issue['commentsCount']: comments = await self.connection.get_comments(issue['id']) for comment in comments: posted = await db.check_comment(conn, comment) if posted: continue updated = comment.get('updated', comment.get('created', '0')) updated = int(updated) if updated <= last_updated: self.logger.info('Skipping old comment %s', comment['id']) continue last_checked = max(last_checked, updated) await self.post_comment(comment) changes = await self.connection.get_changes_for_issue(issue['id']) for change in changes: print(change) updated = int(change.updated) if updated <= last_updated: self.logger.info('Skipping old change for issue %s', issue['id']) continue last_checked = max(last_checked, updated) await self.post_change(issue, change) if last_checked > 0: last_checked = datetime.datetime.fromtimestamp(last_checked / 1000) await db.set_last_updated(conn, project_id, last_checked) async def check_project(self, project, limit=50): self.logger.info("Checking project %s", project['youtrack_id']) try: timestamp = project['last_checked'].timestamp() current = 0 while True: issues = await self.connection.get_issues( project['youtrack_id'], project['search_query'], current, limit, updated_after=int(timestamp * 1000) if timestamp > 0 else None) self.logger.info("Got %s issues in project %s", len(issues), project['youtrack_id']) current += limit issues_tasks = list( self.check_issue(issue, timestamp * 1000) for issue in issues) if issues_tasks: await asyncio.wait(issues_tasks) if len(issues) < limit: break except ssl.SSLError: self.logger.exception('SSLError') return except youtrack.YouTrackException as ex: self.logger.exception('Youtrack exception') return async def check_for_updates(self, interval): """ Iterates over projects looking for updated issues """ self.logger.info("Checking projects for updates") try: async with self.db_pool.acquire() as conn: tasks = [] for project in await db.get_projects(conn): tasks.append(self.check_project(project)) await asyncio.wait(tasks) except asyncio.CancelledError: self.logger.info("cancelled") self.logger.info("Done")
#!/usr/bin/env python3 from aiotg import Bot, Chat from config import TG_TOKEN bot = Bot(api_token=TG_TOKEN) @bot.command(r"ping") def ping(chat: Chat, match): return chat.reply('pong') if __name__ == "__main__": bot.run()
class CamBot: def __init__(self, agent: 'gphotos.GooglePhotosManager', manager: vkmanager.VKManager): self._bot = Bot(conf.bot_token, proxy=conf.tele_proxy) self.session = self._bot.session self.loop = self._bot.loop self.menu_markup = Menu() self.init_handlers() self.agent = agent self.vk_manager = manager def init_handlers(self): self._bot.add_command(r'/mov (.+) (.+)', self.mov) self._bot.add_command(r'/push_vk (.+) (.+)', self.push_vk) self._bot.add_command(r'/check (.+) (.+)', self.check_album) self._bot.add_command(r'/full_check (.+)', self.full_check) self._bot.add_command(r'/clear (.+)', self.clear_command) self._bot.add_command(r'/reg', reg) self._bot.add_command(r'/ch', self.reg_channel) self._bot.add_command(r'/photo_reg', self.reg_photo_channel) self._bot.add_command(r'/menu', self.menu) self._bot.add_command(r'/all', self.img_all_cams) self._bot.add_command(r'/stats (.+)', self.stats_command) self._bot.add_command(r'/stats', self.stats_command) self._bot.add_command(r'/lstats (.+)', self.lstats_command) self._bot.add_command(r'/lstats', self.lstats_command) self._bot.add_command(r'/dbdata', self.db_data) self._bot.add_command(r'/daily', self.daily_movie_group_command) self._bot.add_command(r'/push_on', self.push_vk_on) self._bot.add_command(r'/push_off', self.push_vk_off) self._bot.add_callback(r'regular (.+)', regular) self._bot.add_callback(r'today (.+)', today) self._bot.add_callback(r'weekly (.+)', weekly) self._bot.add_callback(r'select (.+)', self.select) self._bot.add_callback(r'back', self.back) self._bot.add_callback(r'img (.+)', self.img_callback) self._bot.add_callback(r'choose_cam (.+)', self.choose_cam_callback) self._bot.add_callback(r'choose_photo_cam (.+)', self.choose_photo_cam_callback) self._bot.add_callback(r'sync (.+)', self.sync_gphotos) self._bot.add_callback(r'gsnc (.+)', self.run_sync_gphotos) self._bot.add_callback(r'remove (.+)', self.remove_folder) self._bot.add_callback(r'post (.+) (.+)', self.post_photo) self._bot.add_callback(r'clear_cb (.+)', self.clear_callback) self._bot.add_callback(r'check_cb (.+)', self.full_check_callback) self._bot.callback(unhandled_callbacks) def stop(self): self._bot.stop() @ThreadSwitcherWithDB.optimized async def daily_stats(self): await self.stats_request(pendulum.yesterday(), self.notify_admins) @ThreadSwitcherWithDB.optimized async def daily_movie(self, cam: Cam): day = datetime.datetime.now() - datetime.timedelta(days=1) day = day.strftime('%d_%m_%Y') loop = asyncio.get_event_loop() with concurrent.futures.ThreadPoolExecutor() as pool: try: clip = await loop.run_in_executor(pool, lambda: make_movie(cam, day)) except FileNotFoundError as exc: logger.exception(exc) await self.notify_admins( f'File {exc.filename} not found for daily movie {cam.name}: {day}' ) return except Exception as exc: logger.exception(exc) await self.notify_admins( f'Error during making daily movie for {cam.name}: {day}') return if cam.update_channel: async with db_in_thread(): channels = db.query(Channel).filter( Channel.cam == cam.name).all() for channel in channels: await send_video(Chat(self._bot, channel.chat_id), clip) await self.notify_admins(f'Daily movie for {cam.name}: {day} ready!') for chat in await self.admin_chats(): await send_video(Chat(self._bot, chat.chat_id), clip) async def push_vk(self, chat, match): cam = await get_cam(match.group(1), chat) if not cam: return day = match.group(2) path = Path(conf.root_dir ) / 'data' / cam.name / 'regular' / 'clips' / f'{day}.mp4' if not path.exists(): await chat.send_text('Movie file does not exist!') return try: await self.vk_manager.new_post(cam.name, str(path), day.replace('_', ' '), day.replace('_', '/')) except vkmanager.VKManagerError as exc: logger.exception('Error during pushing video to vk') await chat.send_text(exc.detail) except Exception: logger.exception('Unhandled exception during pushing video to vk') await chat.send_text('Unhandled error!') await chat.send_text('Movie successfully published') async def mov(self, chat, match): """ Make movie for specified cam and day. Example: /mov favcam 25_04_2019 :param chat: :param match: :return: """ cam = await get_cam(match.group(1), chat) if not cam: return day = match.group(2) loop = asyncio.get_event_loop() with concurrent.futures.ThreadPoolExecutor() as pool: try: clip = await loop.run_in_executor(pool, lambda: make_movie(cam, day)) except Exception: logger.exception('Error during movie request') await self.notify_admins( f'Error during movie request {day} {cam.name}') return await self.notify_admins(f'Video ready. Uploading..') with open(clip.path, 'rb') as clip: await chat.send_video(clip) async def daily_movie_group(self): for cam in sorted(conf.cameras_list, key=lambda k: k.offset): if cam.render_daily: await self.daily_movie(cam) await self.daily_stats() async def daily_photo_group(self): for cam in conf.cameras_list: image = await CamHandler(cam, self._bot.session).get_img(regular=False) if not image: await self.notify_admins( f'Error during image request for {cam.name}') continue path = image.original_path if cam.resize else image.path await self._post_photo(cam, path) async def daily_movie_group_command(self, chat, match): logger.info('Forced daily movie group command') await self.daily_movie_group() async def push_vk_on(self, chat: Chat, match): loop = asyncio.get_event_loop() cmd = f'systemctl --user start {conf.vk_service}'.split() try: await loop.run_in_executor(None, lambda: subprocess_call(cmd)) except Exception: msg = 'Error during starting vk push service' logger.exception(msg) await chat.send_text(msg) return await chat.send_text('vk push service started') async def push_vk_off(self, chat: Chat, match): loop = asyncio.get_event_loop() cmd = f'systemctl --user stop {conf.vk_service}'.split() try: await loop.run_in_executor(None, lambda: subprocess_call(cmd)) except Exception: msg = 'Error during stopping vk push service' logger.exception(msg) await chat.send_text(msg) return await chat.send_text('vk push service stopped') async def img_all_cams(self, chat: Chat, match): for cam in conf.cameras_list: await self.img_handler(chat, cam) async def img_handler(self, chat: Chat, cam): image = await CamHandler(cam, self._bot.session).get_img(regular=False) if not image: await chat.send_text(f'Error during image request for {cam.name}') return path = image.original_path if cam.resize else image.path markup = Markup([[ InlineKeyboardButton(text='post', callback_data=f'post {cam.name} {path.name}') ]]) with open(path, 'rb') as image: await chat.send_photo(image, reply_markup=markup.to_json()) async def img_callback(self, chat, cq, match): await cq.answer() cam = await get_cam(match.group(1), chat) if not cam: return await self.img_handler(chat, cam) @ThreadSwitcherWithDB.optimized async def reg_channel(self, chat: Chat, match): async with db_in_thread(): channel = db.query(Channel).filter( Channel.chat_id == chat.id).one_or_none() if channel: await self.notify_admins(f'Channel {chat.id} already registered!') return await chat.send_text('Choose cam for channel', reply_markup=CamerasChannel().options.to_json()) @ThreadSwitcherWithDB.optimized async def reg_photo_channel(self, chat: Chat, match): async with db_in_thread(): channel = db.query(PhotoChannel).filter( PhotoChannel.chat_id == chat.id).one_or_none() if channel: await self.notify_admins(f'Channel {chat.id} already registered!') return await chat.send_text( 'Choose cam for photo channel', reply_markup=CamerasChannel('choose_photo_cam').options.to_json()) @ThreadSwitcherWithDB.optimized async def choose_cam_callback(self, chat, cq, match): cam = match.group(1) async with db_in_thread(): channel = Channel(chat_id=chat.id, cam=cam) db.add(channel) db.commit() await cq.answer(text=f'Added channel for {cam}') await self.notify_admins(text=f'Added channel {chat.id} for {cam}') @ThreadSwitcherWithDB.optimized async def choose_photo_cam_callback(self, chat, cq, match): cam = match.group(1) async with db_in_thread(): channel = PhotoChannel(chat_id=chat.id, cam=cam) db.add(channel) db.commit() await cq.answer(text=f'Added photo channel for {cam}') await self.notify_admins( text=f'Added photo channel {chat.id} for {cam}') async def post_photo(self, chat, cq, match): cam = match.group(1) photo = match.group(2) cam = conf.cameras[cam] path = Path(conf.root_dir) / 'data' / cam.name / 'imgs' if cam.resize: path /= 'original' path = path / '_'.join(photo.split('_')[:3]) / photo await self._post_photo(cam, path) await cq.answer() @ThreadSwitcherWithDB.optimized async def _post_photo(self, cam: Cam, photo: Path): async with db_in_thread(): channels = db.query(PhotoChannel).filter( PhotoChannel.cam == cam.name).all() for channel in channels: chat = Chat(self._bot, channel.chat_id) with open(photo, 'rb') as ph: await chat.send_photo(ph) @ThreadSwitcherWithDB.optimized async def notify_admins(self, text, **options): async with db_in_thread(): admins = db.query(Admin).all() for admin in admins: await self._bot.send_message(admin.chat_id, text, **options) @ThreadSwitcherWithDB.optimized async def admin_chats(self): async with db_in_thread(): return db.query(Admin).all() async def menu(self, chat, match): await chat.send_text('Menu', reply_markup=self.menu_markup.main_menu.to_json()) async def select(self, chat: Chat, cq, match): await cq.answer() cam = match.group(1) await chat.edit_text(cq.src['message']['message_id'], f'Camera: {cam}', markup=dataclasses.asdict( self.menu_markup.cam_options[cam].markup)) async def back(self, chat, cq, match): await cq.answer() await chat.edit_text(cq.src['message']['message_id'], 'Menu', markup=dataclasses.asdict( self.menu_markup.main_menu)) async def sync_gphotos(self, chat, cq, match): await cq.answer() cam = match.group(1) await chat.edit_text(cq.src['message']['message_id'], f'Choose folder for {cam}', markup=dataclasses.asdict( SyncFolders(cam).folders)) async def run_sync_gphotos(self, chat, cq, match): _folder = match.group(1) folder = Path(conf.root_dir) / 'data' / _folder logger.debug(f'GOING TO SYNC FOLDER {folder}') await cq.answer(text=f'GOING TO SYNC FOLDER {folder}') await self.notify_admins(f'Started sync {folder}') try: await GooglePhotosManager().batch_upload(Path(folder)) except Exception: logger.exception('Sync error!') await self.notify_admins(f'Error with {folder}!') return await self.notify_admins(f'{folder} successfully uploaded!') markup = Markup([[ InlineKeyboardButton(text=f'{_folder}', callback_data=f'remove {_folder}') ]]) await chat.send_text(f'Remove folder {folder.name}', reply_markup=markup.to_json()) async def remove_folder(self, chat, cq, match): await cq.answer(text='Removing folder..') folder = match.group(1) folder = Path(conf.root_dir) / 'data' / folder shutil.rmtree(folder) await chat.send_text('Successfully removed!') async def stats_command(self, chat: Chat, match): try: day = pendulum.from_format(match.group(1), 'DD_MM_YYYY') except IndexError: day = pendulum.today() await self.stats_request(day, chat.send_text) async def lstats_command(self, chat: Chat, match): try: day = pendulum.from_format(match.group(1), 'DD_MM_YYYY') except IndexError: day = pendulum.today() await self.stats_request(day, chat.send_text) @ThreadSwitcherWithDB.optimized async def db_data(self, chat: Chat, match): async with db_in_thread(): md_data = db_data() await chat.send_text('\n'.join(md_data), parse_mode='Markdown') async def stats_request(self, day: pendulum.DateTime, send_command): logger.info(f'Getting stats info for {day}') try: markdown_result = await self.local_stats_handler(day) except Exception: logger.exception('Error during stats request') await send_command('Error during request stats') return day = day.format('DD_MM_YYYY') markup = Markup([[ InlineKeyboardButton(text='check', callback_data=f'check_cb {day}') ], [ InlineKeyboardButton(text='clear', callback_data=f'clear_cb {day}') ]]) await send_command('\n'.join(markdown_result), parse_mode='Markdown', reply_markup=markup.to_json()) async def stats_handler(self, day=None): loop = asyncio.get_event_loop() result = await loop.run_in_executor(None, lambda: stats(day)) album_stats = await self.agent.album_stats(day) markdown_result = [f'#stats *{day.format("DD/MM/YYYY")}*'] for d in result['cameras']: stat = result['cameras'][d] count, size = stat['count'], convert_size(stat['size']) if count: avg = convert_size(stat['size'] / count) else: avg = 0 media_count = album_stats[d] markdown_result.append( f'*{d}*: {count} - {media_count} - {size} - {avg} ') total = convert_size(result['total']) markdown_result.append(f'*total*: {total}') free = convert_size(result['free']) markdown_result.append(f'*free*: {free}') return markdown_result async def local_stats_handler(self, day=None): loop = asyncio.get_event_loop() result = await loop.run_in_executor(None, lambda: stats(day)) markdown_result = [f'#stats *{day.format("DD/MM/YYYY")}*'] for d in result['cameras']: stat = result['cameras'][d] count, size = stat['count'], convert_size(stat['size']) if count: avg = convert_size(stat['size'] / count) else: avg = 0 markdown_result.append(f'*{d}*: {count} - {size} - {avg} ') total = convert_size(result['total']) markdown_result.append(f'*total*: {total}') free = convert_size(result['free']) markdown_result.append(f'*free*: {free}') return markdown_result async def check_album(self, chat, match): cam = await get_cam(match.group(1), chat) if not cam: return day = match.group(2) await self.agent.check_album(cam, day) async def full_check_handler(self, chat, day): logger.info(f'Going to full check for {day}') for cam in conf.cameras_list: try: await self.agent.check_album(cam, day) except Exception: logger.exception( f'Error during check and sync {cam.name} -- {day}') await chat.send_text(f'Error {cam.name} — {day}') continue await chat.send_text(f'Finished with {cam.name} — {day}') msg = f'Finished full check for {day}' logger.info(msg) await chat.send_text(msg) async def full_check(self, chat, match): day = match.group(1) await self.full_check_handler(chat, day) async def full_check_callback(self, chat, cq, match): day = match.group(1) await cq.answer(text=f'Running full check for {day}') await self.full_check_handler(chat, day) async def clear_handler(self, chat, day): logger.info(f'Going to clear for {day}') loop = asyncio.get_event_loop() for cam in conf.cameras_list: try: await loop.run_in_executor(None, lambda: clear_cam_storage(day, cam)) except Exception: logger.exception(f'Error during clear {cam.name} -- {day}') await chat.send_text(f'Error {cam.name} — {day}') continue await chat.send_text(f'Finished with {cam.name} — {day}') logger.info(f'Finished clear for {day}') async def clear_command(self, chat, match): day = match.group(1) await self.clear_handler(chat, day) async def clear_callback(self, chat, cq, match): day = match.group(1) await cq.answer(text=f'Cleaning for {day}') await self.clear_handler(chat, day)
if len(sys.argv) < 2: print('usage: python simple.py simple.conf') sys.exit(1) conf = sys.argv[1] config = configparser.ConfigParser() config.read(conf) BOT_TOKEN = config.get('telegram', 'bot_token', fallback='') ADMIN_ID = int(config.get('telegram', 'admin_id', fallback=0)) PROXY = config.get('telegram', 'proxy', fallback=None) ADDRESS = config.get('ipc', 'address', fallback='http://127.0.0.1:1242/') PASSWORD = config.get('ipc', 'password', fallback='') logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO) logger = logging.getLogger('simple_asf_bot') bot = Bot(api_token=BOT_TOKEN, proxy=PROXY) async def command(asf, cmd): return await asf.Api.Command.post(body={ 'Command': cmd }) async def main(): async with IPC(ipc=ADDRESS, password=PASSWORD) as asf: @bot.command(r'^([^/].*)$') async def message(chat: Chat, match): sender = chat.sender['id'] logger.info(f'Get {match.group(1)}') if sender == ADMIN_ID:
import json import os from aiotg import Bot bot = Bot(api_token=os.environ["API_TOKEN"]) @bot.command(r'/start') def start(chat, match): markup = { 'type': 'InlineKeyboardMarkup', 'inline_keyboard': [ [{ 'type': 'InlineKeyboardButton', 'text': 'Button A', 'callback_data': 'buttonclick-A' }, { 'type': 'InlineKeyboardButton', 'text': 'Button B', 'callback_data': 'buttonclick-B' }], [{ 'type': 'InlineKeyboardButton', 'text': 'Nohandle Button', 'callback_data': 'no_callback_data' }], ] }
import textwrap import random # Bot from aiotg import Bot # Queries from database.queries import user_exists, insert_user, deactivate_user from database.queries import insert_text # Variables api_token = os.environ.get("API_TOKEN") bot_name = os.environ.get("BOT_NAME") # Bot bot = Bot(api_token=api_token, name=bot_name) # Logging logger = logging.getLogger("bot") logging.basicConfig(level=logging.DEBUG) def format_text(text): return textwrap.dedent(text) @bot.handle("new_chat_member") async def new_chat_member_event(chat, member): logger.info("New chat member %s joined group", member["first_name"]) text = format_text(""" {greet}, {name}!
TOKEN = os.environ["PP_BOT_TOKEN"] DB_PATH = os.environ.get("PP_BOT_DB_PATH", os.path.expanduser("~/.tg_pp_bot.db")) GREETING = """ Use /poker task url or description to start game. Multiline is also supported /poker line1 line2 Currently there is only one scale: 1, 2, 3, 5, 8, 13, 20, 40, ❔, ☕ """ bot = Bot(TOKEN) storage = GameRegistry() init_logging() REVEAL_RESTART_COMMANDS = [ Game.OP_REVEAL, Game.OP_RESTART, Game.OP_RESTART_NEW, Game.OP_REVEAL_NEW ] @bot.command("/start") @bot.command("/?help") async def start_poker(chat: Chat, match): await chat.send_text(GREETING) @bot.command("(?s)/poker\s+(.+)$") @bot.command("/(poker)$")
import logging from aioredis import create_redis_pool, Redis from aiotg import Chat, Bot from trading_bot.manager import Manager, State from trading_bot.settings import dev_hooks_token, chatbase_token, proxy_string from trading_bot.sources.sources import SmartLab from trading_bot.telegram_helper import keyboard_markup, build_menu log = logging.getLogger(__name__) bot = Bot( api_token=dev_hooks_token, chatbase_token=chatbase_token, name="TradingNewsBot", proxy=proxy_string, ) manager = None async def init(host): global manager redis: Redis = await create_redis_pool(host, minsize=5, maxsize=10) manager = Manager(redis=redis) async for chat_id, data in manager.check_new_all(save=False): pass
class TeleGate(object): def __init__(self): self.ids = PickleDict('ids') self.members = PickleDict('members') self.ignored = PickleDict('ignored') self.cooldown = defaultdict(lambda: 0) self.dialogs = {} self.tripmap = {} self.bot = Bot(api_token=config.token, default_in_groups=True) for content_type in ['photo', 'video', 'audio', 'voice', 'document', 'sticker']: self.bot.handle(content_type)(self.handle_chat) self.bot.default(self.handle_chat) # self.bot.command(r'/set(icon|name|region|cooldown) (.+)')(self.set_user_prefs) # self.bot.command('help')(self.help) self.bot.command('setup')(self.setup) self.bot.command('start')(self.setup) self.bot.callback(r"setup-(\w+)")(self.setup_button_clicked) async def get_trip_flags(self): async with self.bot.session.get(f'https://{config.url}/js/tripflags.js') as s: data = await s.text() for l in data.splitlines(): if l.startswith('flags_hover_strings'): self.tripmap[l.split('"')[1]] = l.split('"')[3] async def post(self, body, name="Anonymous", convo="General", trip="", file="", country=None): if trip: name = '{}#{}'.format(name, trip) data = { 'chat': config.board, 'name': name, 'trip': trip or '', 'body': body, 'convo': convo, } if country: data['country'] = country if file: data['image'] = open(file, 'rb') await self.bot.session.post( f'https://{config.url}/chat/{config.board}', data=data, cookies={'password_livechan': config.password_livechan} ) async def get_posts(self, last_count=0, limit=30): params = {'count': last_count, 'limit': limit} async with self.bot.session.get(f'https://{config.url}/last/{config.board}', params=params) as s: data = await s.json() data.reverse() return data def send_gif(self, chat, animation, caption="", **options): return self.bot.api_call( "sendAnimation", chat_id=str(chat.id), animation=animation, caption=caption, **options ) def get_member(self, chat): default = Member(chat.message['from']['first_name'], config.default_trip, None, config.default_cooldown) return Member(*self.members.get(chat.message['from']['id'], default)) async def updater(self): last_count = 0 post_data = {'count': 0} while True: try: await asyncio.sleep(config.poll_interval) group = self.bot.group(config.group_id) data = await self.get_posts(last_count) for post_data in data: if post_data['identifier'] in self.ignored: continue if post_data['convo'] == 'General' and post_data['count'] not in self.ids: res = None country2 = emoji_flags.get_flag(post_data['country'].split('-')[0]) if '-' in post_data['country']: country2 = '{}-{}'.format(country2, post_data['country'].split('-')[1]) body = '{} {} {} {}:\n{}'.format(post_data['count'], post_data['name'], self.tripmap.get(post_data.get('trip'), ''), country2, post_data['body']) image = post_data.get('image') reply_to = self.ids.get(int(post_data['body'].lstrip('>').split()[0]) if post_data['body'].startswith('>>') else None) reply_to = {'reply_to_message_id':str(reply_to)} if reply_to else {} if image: image = 'https://{}{}'.format(config.url, image.split('public', 1)[1]) filename = image.split('/')[-1] body = body[:1024] async with self.bot.session.get(image) as f: if f.status == 200: data = await f.read() with open('tmp/{}'.format(filename), 'wb') as f: f.write(data) else: data = None if data: with open('tmp/{}'.format(filename), 'rb') as f: ext = os.path.splitext(image)[1] if ext in ['.png', '.jpg']: res = await group.send_photo(f, caption=body, **reply_to) elif ext in ['.gif']: res = await self.send_gif(group, f, caption=body, **reply_to) elif ext in ['.mp4']: res = await group.send_video(f, caption=body, **reply_to) elif ext in ['.mp3', '.ogg']: res = await group.send_audio(f, caption=body) elif ext == '.webm': body += f'\nhttps://{config.url}/tmp/uploads/' + filename res = await group.send_text(body, **reply_to) os.unlink('tmp/{}'.format(filename)) else: res = await group.send_text(body, **reply_to) elif post_data['body']: res = await group.send_text(body, **reply_to) for st in re.findall(r'\[st\]([\w\d\-\.]+)\[\/st\]', body): path = 'stickers/{}.png'.format(st) if not os.path.exists(path): async with self.bot.session.get(f'https://{config.url}/images/stickers/{st}.png') as f: if f.status == 200: data = await f.read() with open(path, 'wb') as f: f.write(data) with open(path, 'rb') as f: res2 = await group.send_photo(f) if not res: res = res2 if res: self.ids[post_data['count']] = res['result']['message_id'] self.ids[res['result']['message_id']] = post_data['count'] except Exception as e: traceback.print_exc() last_count = post_data['count'] async def handle_chat(self, chat, image): if not chat.is_group(): if chat.message['from']['id'] in self.dialogs: await self.setup(chat, image) return else: if chat.message['from']['id'] not in self.members: await self.setup(chat, image) if type(image) == list: image = image[-1] if 'file_id' in image: cq = chat.message text = chat.message.get('caption', '') else: cq = image text = cq['text'] if 'reply_to_message' in cq: id = cq['reply_to_message']['message_id'] if id in self.ids: text = '>>{}\n{}'.format(self.ids[id], text) id = image.get('file_id') if id: info = await self.bot.get_file(id) path = 'tmp/{}'.format(info['file_path'].split('/')[-1]) if path.endswith('.oga'): path = path.replace('.oga', '.ogg') async with self.bot.download_file(info['file_path']) as res: data = await res.read() open(path, 'wb').write(data) if path.endswith('.webp'): newpath = path.replace('.webp', '.png') os.system('convert {} {}'.format(path, newpath)) # requires imagemagick path = newpath elif path.endswith('.tgs'): newpath = 'stickers/{}.gif'.format(image['file_id']) if not os.path.exists(newpath): import tgs from tgs.exporters import gif a=tgs.parsers.tgs.parse_tgs(path) with open(newpath, 'wb') as f: gif.export_gif(a, f) os.unlink(path) path = newpath else: path = None member = self.get_member(chat) if not (time() > self.cooldown[cq['from']['id']] + member.cooldown_limit): return self.cooldown[cq['from']['id']] = time() await self.post(text, name=member.name, trip=member.trip, country=member.country, file=path) if path and path.startswith('tmp/'): os.unlink(path) await chat.delete_message(cq['message_id']) async def setup(self, chat, match): member = self.get_member(chat) id = chat.message['from']['id'] if chat.is_group(): chat = self.bot.private(chat.message['from']['id']) if id in self.dialogs: setattr(member, {'name': 'name', 'icon': 'trip', 'region': 'country'}[self.dialogs[id]], chat.message['text']) if member.trip == 'none': member.trip = None self.members[chat.message['from']['id']] = member del self.dialogs[id] buttons = [] for button in ['name', 'icon', 'region']: buttons.append({ "type": "InlineKeyboardButton", "text": "Set {}".format(button), "callback_data": "setup-{}".format(button), }) markup = { "type": "InlineKeyboardMarkup", "inline_keyboard": [buttons] } chat.send_text(f"Name: {member.name}\nIcon: {member.trip}\nRegion: {member.country}", reply_markup=json.dumps(markup)) def setup_button_clicked(self, chat, cq, match): if chat.is_group(): chat = self.bot.private(chat.message['from']['id']) id = chat.message['chat']['id'] param = match.group(1) self.dialogs[id] = param example = { 'name': 'Kot', 'icon': 'plkot; none for no icon', 'region': 'PL-77 or RU-47', }[param] chat.send_text('Send your {}(for example: {})'.format(param, example)) def run(self): loop = asyncio.get_event_loop() loop.create_task(self.updater()) loop.create_task(self.get_trip_flags()) self.bot.run()
import os import asyncio from aiohttp import web from aiotg import Bot, Chat, InlineQuery, CallbackQuery from klocmod import LocalizationsContainer import msgdb import strconv from txtproc import TextProcessorsLoader, TextProcessor from txtprocutil import resolve_text_processor_name from data.config import * from queryutil import * DECRYPT_BUTTON_CACHE_TIME = 3600 # in seconds bot = Bot(api_token=TOKEN, default_in_groups=True) localizations = LocalizationsContainer.from_file("app/localizations.ini") text_processors = TextProcessorsLoader(strconv) @bot.command("/start") @bot.command("/help") @bot.default async def start(chat: Chat, _) -> None: lang = localizations.get_lang(chat.message['from'].get('language_code')) await chat.send_text(lang['help_message']) await chat.send_text(lang['help_message_transformers_list']) for processor in text_processors.all_processors: help_message_key = 'help_' + processor.snake_case_name localized_help_message = lang[help_message_key] # skip empty and undefined help messages
# Photos from commands.photos import insert_watermark # from commands.photos import process_photo # Contacts from commands.contacts import process_contact # Inline queries from commands.inline import process_inline_query # Variables api_token = os.environ.get('API_TOKEN') bot_name = os.environ.get('BOT_NAME') # Bot bot = Bot(api_token=api_token, name=bot_name) # Channel channel = bot.channel(os.environ.get('CHANNEL_NAME', '@VodiyBozorTest')) # Logging logger = logging.getLogger('bot') logging.basicConfig(level=logging.DEBUG) @bot.command(r'/start') @bot.command(r'/on') async def start(chat, match): await process_start_command(chat, match, logger) await process_ads_command(chat, match, logger)
import time from concurrent.futures import ThreadPoolExecutor from aiotg import Bot formatter = logging.Formatter('[%(levelname)s] %(message)s (%(asctime)s)') file_handler = logging.FileHandler('torrent-helper.log') stdout_handler = logging.StreamHandler(sys.stdout) file_handler.setFormatter(formatter) stdout_handler.setFormatter(formatter) logger = logging.getLogger('torrent-helper') logger.addHandler(file_handler) logger.addHandler(stdout_handler) logger.setLevel(logging.INFO) t2m_bot = Bot(api_token='') m2t_bot = Bot(api_token='') proc_pool = ThreadPoolExecutor(15) @t2m_bot.command(r'/(start|help)') async def start(chat, match): await chat.send_text('Send torrent file here') @m2t_bot.command(r'/(start|help)') async def start(chat, match): await chat.send_text('Send magnet link here')
```棒棒勝 type:mp3``` ```棒棒勝 type:mpeg``` 若同時想搜尋作者和曲名,請用 `>` 隔開 (預設為作者、曲名都納入搜尋),像這樣: ```棒棒勝>洨安之歌``` 也可以搭配`type`指令,像這樣: ```棒棒勝>洨安之歌 type:flac``` 輸入 `/stats` 來獲取 bot 資訊。 用 `/music` 指令來在群聊內使用棒棒勝 Music Bot,像這樣: ```/music 棒棒勝``` """ not_found = """ 找不到資料 :/ """ bot = Bot(api_token=os.environ.get('API_TOKEN'), name=os.environ.get('BOT_NAME'), botan_token=os.environ.get("BOTAN_TOKEN")) logger = logging.getLogger("musicbot") channel = bot.channel(os.environ.get('CHANNEL')) @bot.handle("audio") async def add_track(chat, audio): if (str(chat.sender) == 'N/A'): sendervar = os.environ.get('CHANNEL_NAME') else: sendervar = str(chat.sender) if (await db.tracks.find_one({"file_id": audio["file_id"]})): await chat.send_text("資料庫裡已經有這首囉 owo") logger.info("%s 傳送了重複的歌曲 %s %s", sendervar,
def __init__(self, config=None, *, context=None, loop=None): super().__init__(config, context=context, loop=loop) self.bot = Bot(api_token=self.config.api_token)
import os import logging from aiotg import Bot, Chat from onvotar import calculate bot = Bot( api_token=os.environ.get("API_TOKEN"), name=os.environ.get("BOT_NAME"), ) AJUT_CAT = ('Per conèixer el teu col·legi electoral, ' 'envia un missatge /vota amb les teves dades ' 'separades per espais i ' 'fent servir aquest format: \n' '/vota DNI DATA_NAIXEMENT CODI_POSTAL\n\n' 'Exemple:\n/vota 00001714N 01/10/2017 01234') AJUT_CAST = ('Para conocer tu colegio electoral, ' 'envía un mensaje /votar con tus datos ' 'separados por espacios y ' 'usando este formato: \n' '/vota DNI DATA_NACIMIENTO CODIGO_POSTAL\n\n' 'Ejemplo:\n/votar 00001714N 01/10/2017 01234') AJUT_ENG = ('To know you voting booth location, ' 'send a message /vote with your data ' 'separated with spaces, using ' 'this format: \n' '/vote DNI BIRTH_DATE ZIP_CODE\n\n' 'Example:\n/vote 00001714N 01/10/2017 01234')
import os from aiotg import Bot, Chat bot = Bot(api_token="API_TOKEN") @bot.command(r"bitcoin") async def bitcoin(chat: Chat, match): url = "https://api.bitcoinaverage.com/ticker/global/USD/" async with bot.session.get(url) as s: info = await s.json() await chat.send_text(info["24h_avg"]) if __name__ == "__main__": bot.run(debug=True)
import json import os import datetime import asyncio from aiotg import Bot, Chat from rzd.aiorzd import RzdFetcher, UpstreamError from rzd.config import QUERY_REGEXP_LIST from rzd.helper import logger, QueryString, NotifyExceptions os.environ.setdefault('BOT_CONFIG', 'rzd/config.json') with open(os.environ['BOT_CONFIG']) as cfg: config = json.load(cfg) bot = Bot(config['API_TOKEN'], name=config['BOT_NAME']) def multibot(command, default=False): def decorator(fn): for r in QUERY_REGEXP_LIST: fn = bot.command(r'/%s@%s\s+%s' % (command, bot.name, r))(fn) fn = bot.command(r'/%s\s+%s' % (command, r))(fn) if default: fn = bot.command(r'@%s\s+%s' % (bot.name, r))(fn) return fn return decorator
import os from aiotg import Bot, Chat from telegram import ReplyKeyboardMarkup, KeyboardButton from photo import make_photo logging.basicConfig( level=getattr(logging, os.environ.get("BOT_LOGGING_LEVEL", "DEBUG")), format="%(asctime)s | %(name)s | %(levelname)s - %(message)s", ) logger = logging.getLogger(__name__) ch = logging.StreamHandler() logger.addHandler(ch) bot = Bot(api_token=os.environ["BOT_TOKEN"], ) def get_admins(): return map(int, os.environ.get("BOT_ADMINS", "").split(",")) def is_authorized(sender): return sender["id"] in get_admins() def get_button(): return json.dumps( ReplyKeyboardMarkup( [[KeyboardButton(text="Photo")]], resize_keyboard=True,
MATRIX_HOST_BARE = CONFIG['hosts']['bare'] MATRIX_PREFIX = MATRIX_HOST + '_matrix/client/r0/' MATRIX_MEDIA_PREFIX = MATRIX_HOST + '_matrix/media/r0/' USER_ID_FORMAT = CONFIG['user_id_format'] DATABASE_URL = CONFIG['db_url'] except (OSError, IOError) as exception: print('Error opening config file:') print(exception) exit(1) GOO_GL_URL = 'https://www.googleapis.com/urlshortener/v1/url' TG_BOT = Bot(api_token=TG_TOKEN) MATRIX_SESS = ClientSession() SHORTEN_SESS = ClientSession() def create_response(code, obj): """ Create an HTTP response with a JSON body. :param code: The status code of the response. :param obj: The object to serialize and include in the response. :return: A web.Response. """ return web.Response(text=json.dumps(obj), status=code, content_type='application/json', charset='utf-8')
import json from aiotg import Bot, Chat, CallbackQuery from util import format_message, match_category, \ download_gifs, produce_imgs, log_users from spider import fetch_lists, fetch_img with open('token.json') as t, open('category.json') as c: token = json.loads(t.read()) category = json.loads(c.read()) bot = Bot(**token) root_url = "http://www.gamersky.com/ent/" help_tetx = "点击按钮查看下一页, 或者点击原网址查看详细内容" @bot.command(r"/start") async def list_category(chat: Chat, match): kb, row = [], -1 for idx, item in enumerate(category["name"]): if idx % 2 == 0: kb.append([]) row += 1 kb[row].append(item) keyboard = { "keyboard": kb, "resize_keyboard": True } text = "请选择你喜欢的图片类型,动态图和冷知识还有bug,其他类型都能正常查看" message = await chat.send_text(text=text, reply_markup=json.dumps(keyboard)) await log_users(message)
import os from aiotg import Bot, Chat bot = Bot(os.environ["API_TOKEN"]) @bot.command(r"bitcoin") async def bitcoin(chat: Chat, match): url = "https://api.bitcoinaverage.com/ticker/global/USD/" async with bot.session.get(url) as s: info = await s.json() await chat.send_text(info["24h_avg"]) if __name__ == '__main__': bot.run(debug=True)
import os from aiotg import Bot, Chat import imageio from services import ClassifyModel, get_square model = ClassifyModel() bot = Bot(api_token=os.getenv("TG_TOKEN")) @bot.command("/start") async def start(chat: Chat, match): return chat.reply("Send me photo of ant or bee.") @bot.handle("photo") async def handle_photo(chat: Chat, photos): # Get image binary data meta = await bot.get_file(photos[-1]["file_id"]) resp = await bot.download_file(meta["file_path"]) data = await resp.read() # Convert binary data to numpy.ndarray image image = imageio.imread(data) # Do the magic tag = await model.predict.call(image) # Simple text response await chat.reply(f"I think this is {tag} ...")
from aiotg import Bot, Chat from db import UserData bot = Bot("518654004:AAGeHYTzXDd-gRN-2LwjUeljN4nghIM3F34") async def greeter(private_id, body): private = bot.private(private_id) await private.send_text(body) @bot.command(r"/whoami") async def whoami(chat, match): return chat.reply(chat.sender["id"]) @bot.command(r"/create (.+)") async def create_new_app(chat: Chat, match): private_id = chat.sender["id"] app_name = str(match.group(1)) print(str(private_id) + " " + str(app_name)) if UserData.select().where(UserData.app_name == app_name): return chat.reply("App with this name already exists") else: UserData.create(app_name=app_name, private_id=private_id).save() return chat.reply("App " + str(app_name) + " successfully create") if __name__ == '__main__': bot.run(debug=True)
import os from enum import Enum from urllib.parse import quote import sys from aiotg import Bot, aiohttp from utils import html_decode bot = Bot(api_token=os.environ['BOT_TOKEN']) advice_api_url = "http://f*****g-great-advice.ru/api/{}" sound_url = 'http://f*****g-great-advice.ru/files/sounds/{}' # sound_9.MP3 dummy_image = 'https://dummyimage.com/1000x200/000000/ffffff.png&text={}' class Advice: def __init__(self, id, text, sound, stat=0): self._id = id self.text = html_decode(text) self.sound = sound self.stat = stat @property def sound_url(self): return sound_url.format(self.sound) @property def photo_url(self): return dummy_image.format(quote(self.text))
class TelegramAgent(Agent): def __init__(self, name=None): super().__init__(name=name) self._bot = Bot(api_token=TELEGRAM_BOT_API_TOKEN, name=TELEGRAM_BOT_NAME, api_timeout=10) self._bot.default(self.on_incoming_telegram_message) self.sent_telegram_messages = {} def on_incoming_telegram_message(self, chat, message): try: text = message['text'] name = text if len(name) > FRAME_NAME_MAX_LENGTH: name = name[:FRAME_NAME_MAX_LENGTH - 1] + '…' full_name = '{} {}'.format(message['from']['first_name'], message['from']['last_name']) sent_msg = self.message(name, data={ 'text': text, 'message_id': message['message_id'], 'chat_id': message['chat']['id'], 'user_id': message['from']['id'], 'username': message['from']['username'], 'full_name': full_name, 'session': message['chat']['id'], }) self.sent_telegram_messages.update( {sent_msg.id: message['chat']['id']}) except Exception: print(traceback.format_exc()) print(message.name, message.data) @on_message('*') async def telegram_message_reply(self, message): if message.reply_to not in self.sent_telegram_messages: return text = message.data.text or message.name try: chat_id = self.sent_telegram_messages[message.reply_to] # del self.sent_telegram_messages[message.reply_to] await self._bot.send_message(chat_id=chat_id, text=text) self.emit('telegram_reply_sent', data={ 'text': text, 'chat_id': chat_id }) except Exception: print(traceback.format_exc()) print(message.name, message.data) @on_event('send_telegram_message') async def send_message(self, event): text = event.data.text chat_id = event.data.session or event.data.chat_id if not chat_id: self.emit('telegram_message_error', data={'text': 'session or chat_id expected.'}) return try: ret_val = await self._bot.send_message(chat_id=chat_id, text=text) self.emit('telegram_message_sent', data={ 'text': text, 'chat_id': chat_id, 'return': ret_val }) except Exception: print(traceback.format_exc()) print(event.name, event.data) @on_event('*** started') def on_started(self, event): self.emit('telegram_connecting') self.spawn(self._bot.loop()) @on_event('*** stopping') def on_stopping(self, event): self._bot.stop()