repeater.py 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # repeater.py
  4. # Copyright (C) 2018-2021 github.com/googlehosts Group:Z
  5. #
  6. # This module is part of googlehosts/telegram-repeater and is released under
  7. # the AGPL v3 License: https://www.gnu.org/licenses/agpl-3.0.txt
  8. #
  9. # This program is free software: you can redistribute it and/or modify
  10. # it under the terms of the GNU Affero General Public License as published by
  11. # the Free Software Foundation, either version 3 of the License, or
  12. # any later version.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU Affero General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU Affero General Public License
  20. # along with this program. If not, see <https://www.gnu.org/licenses/>.
  21. from __future__ import annotations
  22. import asyncio
  23. import gettext
  24. import json
  25. import logging
  26. import re
  27. import sys
  28. import time
  29. import traceback
  30. from configparser import ConfigParser
  31. from typing import Callable, Dict, Mapping, Optional, Tuple, TypeVar, Union
  32. import aioredis
  33. import coloredlogs
  34. import pyrogram
  35. import pyrogram.errors
  36. from pyrogram import Client, ContinuePropagation, filters, raw
  37. from pyrogram.handlers import CallbackQueryHandler, MessageHandler
  38. from pyrogram.types import (CallbackQuery, ChatPermissions,
  39. InlineKeyboardButton, InlineKeyboardMarkup,
  40. Message, User)
  41. import utils
  42. from customservice import CustomServiceBot, JoinGroupVerify
  43. from utils import AuthSystem, PgSQLdb
  44. from utils import TextParser as tp
  45. from utils import get_language
  46. config = ConfigParser()
  47. config.read('config.ini')
  48. logger = logging.getLogger('telegram-repeater').getChild('main')
  49. translation = gettext.translation('repeater', 'translations/',
  50. languages=[get_language()], fallback=True)
  51. _T = translation.gettext
  52. _cT = TypeVar('_cT')
  53. class TextParser(tp):
  54. bot_username = ''
  55. def __init__(self, msg: Message):
  56. self._msg = self.BuildMessage(msg)
  57. self.parsed_msg = self.parse_main()
  58. if msg.chat.id == config.getint('fuduji', 'fudu_group') and \
  59. self.parsed_msg and self.parsed_msg.startswith('\\//'):
  60. self.parsed_msg = self.parsed_msg[1:]
  61. if msg.chat.id == config.getint('fuduji', 'target_group') and self.parsed_msg:
  62. self.parsed_msg = self.parsed_msg.replace(
  63. f'@{TextParser.bot_username}', f"@{config['fuduji']['replace_to_id']}")
  64. _problemT = TypeVar('_problemT', Dict, str, bool, int)
  65. def external_load_problem_set() -> Dict[str, _problemT]:
  66. try:
  67. with open('problem_set.json', encoding='utf8') as fin:
  68. problem_set = json.load(fin)
  69. if len(problem_set['problems']['problem_set']) == 0:
  70. logger.warning('Problem set length is 0')
  71. except:
  72. logger.exception('Error in reading problem set!')
  73. problem_set = {}
  74. return problem_set
  75. class WaitForDelete:
  76. def __init__(self, client: Client, chat_id: int, message_ids: Union[int, Tuple[int, ...]]):
  77. self.client: Client = client
  78. self.chat_id: int = chat_id
  79. self.message_ids: Union[int, Tuple[int, ...]] = message_ids
  80. async def run(self) -> None:
  81. await asyncio.sleep(5)
  82. await self.client.delete_messages(self.chat_id, self.message_ids)
  83. def __call__(self) -> None:
  84. asyncio.run_coroutine_threadsafe(self.run(), asyncio.get_event_loop())
  85. class OperationTimeoutError(Exception):
  86. """Raise this exception if operation time out"""
  87. class OperatorError(Exception):
  88. """Raise this exception if operator mismatch"""
  89. class BotController:
  90. class ByPassVerify(UserWarning):
  91. pass
  92. def __init__(self):
  93. # self.problems_load()
  94. logger.debug('Loading bot configure')
  95. self.target_group: int = config.getint('fuduji', 'target_group')
  96. self.fudu_group: int = config.getint('fuduji', 'fudu_group')
  97. self.bot_id: int = int(config['account']['api_key'].split(':')[0])
  98. self.app: Client = Client(
  99. session_name='session',
  100. api_id=config['account']['api_id'],
  101. api_hash=config['account']['api_hash'],
  102. app_version='repeater'
  103. )
  104. self.botapp: Client = Client(
  105. session_name='beyondbot',
  106. api_id=config['account']['api_id'],
  107. api_hash=config['account']['api_hash'],
  108. bot_token=config['account']['api_key'],
  109. )
  110. logger.debug('Loading other configure')
  111. self.conn: Optional[PgSQLdb] = None
  112. self._redis: Optional[aioredis.Redis] = None
  113. self.auth_system: Optional[AuthSystem] = None
  114. self.warn_evidence_history_channel: int = config.getint('fuduji', 'warn_evidence', fallback=0)
  115. self.join_group_verify_enable: bool = config.getboolean('join_group_verify', 'enable', fallback=True)
  116. self.custom_service_enable: bool = config.getboolean('custom_service', 'enable', fallback=True)
  117. self.join_group_verify: Optional[JoinGroupVerify] = None
  118. self.revoke_tracker_coro: Optional[utils.InviteLinkTracker] = None
  119. self.custom_service: Optional[CustomServiceBot] = None
  120. self.problem_set: Optional[Mapping[str, _problemT]] = None
  121. self.init_handle()
  122. logger.debug('Service status: join group verify: %s, custom service: %s',
  123. self.join_group_verify_enable, self.custom_service_enable)
  124. logger.debug('__init__ method completed')
  125. async def init_connections(self) -> None:
  126. self._redis = await aioredis.create_redis_pool('redis://localhost')
  127. self.conn = await PgSQLdb.create(
  128. config['pgsql']['host'],
  129. config.getint('pgsql', 'port'),
  130. config['pgsql']['user'],
  131. config['pgsql']['passwd'],
  132. config['pgsql']['database']
  133. )
  134. self.auth_system = await AuthSystem.initialize_instance(self.conn, config.getint('account', 'owner'))
  135. if self.join_group_verify_enable:
  136. self.join_group_verify = await JoinGroupVerify.create(self.conn, self.botapp, self.target_group,
  137. self.fudu_group, external_load_problem_set,
  138. self._redis)
  139. self.join_group_verify.init()
  140. self.revoke_tracker_coro = self.join_group_verify.revoke_tracker_coro
  141. if self.custom_service_enable:
  142. self.custom_service = CustomServiceBot(config, self.conn, self.join_group_verify.send_link, self._redis)
  143. @classmethod
  144. async def create(cls) -> BotController:
  145. self = BotController()
  146. await self.init_connections()
  147. return self
  148. def init_handle(self) -> None:
  149. self.app.add_handler(
  150. MessageHandler(self.handle_edit, filters.chat(self.target_group) & ~filters.user(
  151. self.bot_id) & filters.edited))
  152. self.app.add_handler(
  153. MessageHandler(self.handle_new_member, filters.chat(self.target_group) & filters.new_chat_members))
  154. self.app.add_handler(
  155. MessageHandler(self.handle_service_messages, filters.chat(self.target_group) & filters.service))
  156. self.app.add_handler(
  157. MessageHandler(
  158. self.handle_all_media,
  159. filters.chat(self.target_group) & ~filters.user(self.bot_id) & (
  160. filters.photo | filters.video | filters.document | filters.animation | filters.voice)
  161. )
  162. )
  163. self.app.add_handler(MessageHandler(self.handle_dice, filters.chat(self.target_group) & ~filters.user(
  164. self.bot_id) & filters.media))
  165. self.app.add_handler(MessageHandler(self.handle_sticker, filters.chat(self.target_group) & ~filters.user(
  166. self.bot_id) & filters.sticker))
  167. self.app.add_handler(MessageHandler(self.handle_speak, filters.chat(self.target_group) & ~filters.user(
  168. self.bot_id) & filters.text))
  169. self.app.add_handler(MessageHandler(self.handle_incoming, filters.incoming & filters.chat(self.fudu_group)))
  170. self.botapp.add_handler(
  171. MessageHandler(self.handle_bot_send_media, filters.chat(self.fudu_group) & filters.command('SendMedia')))
  172. self.botapp.add_handler(CallbackQueryHandler(self.handle_callback))
  173. async def init(self) -> None:
  174. while not self.botapp.is_connected:
  175. await asyncio.sleep(.5)
  176. TextParser.bot_username = (await self.botapp.get_me()).username
  177. @staticmethod
  178. async def idle() -> None:
  179. await pyrogram.idle()
  180. async def start(self) -> None:
  181. await asyncio.gather(self.app.start(), self.botapp.start())
  182. if self.custom_service_enable:
  183. asyncio.run_coroutine_threadsafe(self.custom_service.start(), asyncio.get_event_loop())
  184. await self.init()
  185. async def stop(self) -> None:
  186. task_pending = []
  187. if self.join_group_verify_enable:
  188. self.revoke_tracker_coro.request_stop()
  189. await self.revoke_tracker_coro.join(1.5)
  190. if self.revoke_tracker_coro.is_alive:
  191. logger.warning('revoke_tracker_coroutine still running!')
  192. if self.custom_service_enable:
  193. task_pending.append(asyncio.create_task(self.custom_service.stop()))
  194. task_pending.append(asyncio.create_task(self.botapp.stop()))
  195. task_pending.append(asyncio.create_task(self.app.stop()))
  196. await asyncio.wait(task_pending)
  197. task_pending.clear()
  198. if self.join_group_verify_enable:
  199. await self.join_group_verify.problems.destroy()
  200. self._redis.close()
  201. await asyncio.gather(self.conn.close(), self._redis.wait_closed())
  202. async def handle_service_messages(self, _client: Client, msg: Message) -> None:
  203. if msg.pinned_message:
  204. text = self.get_file_type(msg.pinned_message)
  205. if text == 'text':
  206. text = msg.pinned_message.text[:20]
  207. else:
  208. text = f'a {text}'
  209. await self.conn.insert_ex(
  210. (await self.botapp.send_message(
  211. self.fudu_group, f'Pined \'{text}\'',
  212. disable_web_page_preview=True,
  213. reply_markup=InlineKeyboardMarkup(inline_keyboard=[
  214. [InlineKeyboardButton(text='UNPIN', callback_data='unpin')]
  215. ]))
  216. ).message_id,
  217. msg.message_id
  218. )
  219. elif msg.new_chat_title:
  220. await self.conn.insert_ex(
  221. (await self.botapp.send_message(self.fudu_group,
  222. f'Set group title to <code>{msg.new_chat_title}</code>',
  223. 'html', disable_web_page_preview=True)).message_id,
  224. msg.message_id
  225. )
  226. else:
  227. logger.info('Got unexpect service message: %s', repr(msg))
  228. async def generate_warn_message(self, user_id: int, reason: str) -> str:
  229. return _T('You were warned.(Total: {})\nReason: <pre>{}</pre>').format(
  230. await self.conn.query_warn_by_user(user_id), reason)
  231. async def process_incoming_command(self, client: Client, msg: Message) -> None:
  232. r = re.match(r'^/bot (on|off)$', msg.text)
  233. if r is None:
  234. r = re.match(r'^/b?(on|off)$', msg.text)
  235. if r:
  236. if not self.auth_system.check_ex(
  237. msg.reply_to_message.from_user.id if msg.reply_to_message else msg.from_user.id): return
  238. await self.auth_system.mute_or_unmute(
  239. r.group(1),
  240. msg.reply_to_message.from_user.id if msg.reply_to_message else msg.from_user.id
  241. )
  242. await msg.delete()
  243. if msg.text == '/status':
  244. user_id = msg.reply_to_message.from_user.id if msg.reply_to_message else msg.from_user.id
  245. status = [str(user_id), ' summary:\n\n', 'A' if self.auth_system.check_ex(user_id) else 'Una',
  246. 'uthorized user\nBot status: ',
  247. CustomServiceBot.return_bool_emoji(not self.auth_system.check_muted(user_id))]
  248. WaitForDelete(client, msg.chat.id,
  249. (msg.message_id, (await msg.reply(''.join(status), True)).message_id))()
  250. del status
  251. elif msg.text.startswith('/promote'):
  252. if len(msg.text.split()) == 1:
  253. if msg.reply_to_message is None or not self.auth_system.check_ex(msg.reply_to_message.from_user.id):
  254. await self.botapp.send_message(msg.chat.id, 'Please reply to an Authorized user.',
  255. reply_to_message_id=msg.message_id)
  256. return
  257. user_id = msg.reply_to_message.from_user.id
  258. else:
  259. user_id = int(msg.text.split()[1])
  260. await self.botapp.send_message(
  261. msg.chat.id,
  262. 'Please use bottom to make sure you want to add {} to Administrators'.format(
  263. TextParser.parse_user_markdown(user_id)),
  264. parse_mode='markdown',
  265. reply_to_message_id=msg.message_id,
  266. reply_markup=InlineKeyboardMarkup(inline_keyboard=[
  267. [
  268. InlineKeyboardButton(
  269. text='Yes, confirm',
  270. callback_data=f'promote {user_id}'
  271. )
  272. ],
  273. [
  274. InlineKeyboardButton(text='Cancel', callback_data='cancel d')
  275. ]
  276. ]))
  277. return
  278. elif msg.text.startswith('/su'):
  279. if not self.auth_system.check_ex(msg.from_user.id):
  280. return
  281. await self.botapp.promote_chat_member(
  282. self.target_group,
  283. int(msg.from_user.id),
  284. True,
  285. can_delete_messages=True,
  286. can_pin_messages=True,
  287. can_promote_members=True
  288. )
  289. await self.botapp.send_message(
  290. msg.chat.id,
  291. 'Access Granted',
  292. disable_notification=True,
  293. reply_to_message_id=msg.message_id
  294. )
  295. elif msg.text.startswith('/title'):
  296. if not self.auth_system.check_ex(msg.from_user.id):
  297. return
  298. await self.botapp.set_chat_title(
  299. self.target_group,
  300. msg.text.split(maxsplit=2)[1]
  301. )
  302. if msg.reply_to_message:
  303. if msg.text == '/del':
  304. message_id = await self.conn.get_reply_id_reverse(msg)
  305. if message_id is None:
  306. await self.botapp.send_message(msg.chat.id, 'MESSAGE_ID_NOT_FOUND',
  307. reply_to_message_id=msg.message_id)
  308. return
  309. try:
  310. await client.forward_messages(msg.chat.id, self.target_group, message_id)
  311. except:
  312. await client.send_message(msg.chat.id, traceback.format_exc(), disable_web_page_preview=True)
  313. try:
  314. await self.botapp.delete_messages(self.target_group, message_id)
  315. await client.delete_messages(self.fudu_group, [msg.message_id, msg.reply_to_message.message_id])
  316. except:
  317. pass
  318. elif msg.text == '/getid':
  319. user_id = await self.conn.get_user_id(msg)
  320. await msg.reply(
  321. 'user_id is `{}`'.format(
  322. user_id['user_id'] if user_id is not None and user_id['user_id'] else 'ERROR_INVALID_USER_ID'
  323. ),
  324. parse_mode='markdown'
  325. )
  326. elif msg.text == '/get' and await self.conn.get_reply_id_reverse(msg):
  327. try:
  328. await client.forward_messages(self.fudu_group, self.target_group,
  329. await self.conn.get_reply_id_reverse(msg))
  330. except:
  331. await client.send_message(msg.chat.id, traceback.format_exc().splitlines()[-1])
  332. elif msg.text == '/fw':
  333. message_id = await self.conn.get_reply_id_reverse(msg)
  334. if message_id is None:
  335. await msg.reply('ERROR_INVALID_MESSAGE_ID')
  336. return
  337. await self.conn.insert_ex(
  338. (await self.botapp.forward_messages(self.target_group, self.target_group, message_id)).message_id,
  339. msg.message_id)
  340. elif msg.text.startswith('/ban'):
  341. user_id = await self.conn.get_user_id(msg)
  342. if len(msg.text) == 4:
  343. restrict_time = 0
  344. else:
  345. r = re.match(r'^([1-9]\d*)([smhd])$', msg.text[5:])
  346. if r is not None:
  347. restrict_time = int(r.group(1)) * {'s': 1, 'm': 60, 'h': 60 * 60, 'd': 60 * 60 * 24}.get(
  348. r.group(2))
  349. else:
  350. await self.botapp.send_message(msg.chat.id, 'Usage: `/ban` or `/ban <Duration>`',
  351. 'markdown', reply_to_message_id=msg.message_id)
  352. return
  353. if user_id is not None and user_id['user_id']:
  354. if user_id['user_id'] not in self.auth_system.whitelist:
  355. await self.botapp.send_message(
  356. msg.chat.id,
  357. 'What can {} only do? Press the button below.\n'
  358. 'This confirmation message will expire after 20 seconds.'.format(
  359. TextParser.parse_user_markdown(user_id['user_id'])
  360. ),
  361. reply_to_message_id=msg.message_id,
  362. parse_mode='markdown',
  363. reply_markup=InlineKeyboardMarkup(
  364. inline_keyboard=[
  365. [
  366. InlineKeyboardButton(
  367. text='READ',
  368. callback_data=f"res {restrict_time} read {user_id['user_id']}")
  369. ],
  370. [
  371. InlineKeyboardButton(
  372. text='SEND_MESSAGES',
  373. callback_data=f"res {restrict_time} write {user_id['user_id']}"),
  374. InlineKeyboardButton(
  375. text='SEND_MEDIA',
  376. callback_data=f"res {restrict_time} media {user_id['user_id']}")
  377. ],
  378. [
  379. InlineKeyboardButton(
  380. text='SEND_STICKERS',
  381. callback_data=f"res {restrict_time} stickers {user_id['user_id']}"),
  382. InlineKeyboardButton(
  383. text='EMBED_LINKS',
  384. callback_data=f"res {restrict_time} link {user_id['user_id']}")
  385. ],
  386. [
  387. InlineKeyboardButton(text='Cancel', callback_data='cancel')
  388. ]
  389. ]
  390. )
  391. )
  392. else:
  393. await self.botapp.send_message(
  394. msg.chat.id,
  395. 'ERROR_WHITELIST_USER_ID',
  396. reply_to_message_id=msg.message_id
  397. )
  398. else:
  399. await self.botapp.send_message(
  400. msg.chat.id,
  401. 'ERROR_INVALID_USER_ID',
  402. reply_to_message_id=msg.message_id
  403. )
  404. elif msg.text == '/kick':
  405. user_id = await self.conn.get_user_id(msg)
  406. if user_id is not None and user_id['user_id']:
  407. if user_id['user_id'] not in self.auth_system.whitelist:
  408. await self.botapp.send_message(
  409. msg.chat.id,
  410. 'Do you really want to kick {}?\n'
  411. 'If you really want to kick this user, press the button below.\n'
  412. 'This confirmation message will expire after 15 seconds.'.format(
  413. TextParser.parse_user_markdown(user_id['user_id'])
  414. ),
  415. reply_to_message_id=msg.message_id,
  416. parse_mode='markdown',
  417. reply_markup=InlineKeyboardMarkup(
  418. inline_keyboard=[
  419. [
  420. InlineKeyboardButton(
  421. text='Yes, kick it',
  422. callback_data=f'kick {msg.from_user.id} '
  423. f'{user_id["user_id"]}'
  424. )
  425. ],
  426. [
  427. InlineKeyboardButton(
  428. text='No',
  429. callback_data='cancel'
  430. )
  431. ],
  432. ]
  433. )
  434. )
  435. else:
  436. await self.botapp.send_message(msg.chat.id, 'ERROR_WHITELIST_USER_ID',
  437. reply_to_message_id=msg.message_id)
  438. else:
  439. await self.botapp.send_message(msg.chat.id, 'ERROR_INVALID_USER_ID',
  440. reply_to_message_id=msg.message_id)
  441. elif msg.text.startswith('/pin'):
  442. target_id = await self.conn.get_reply_id_reverse(msg)
  443. if target_id is None:
  444. await msg.reply('ERROR_INVALID_MESSAGE_ID')
  445. return
  446. await self.botapp.pin_chat_message(self.target_group, target_id, not msg.text.endswith('a'))
  447. elif msg.text.startswith('/warn'):
  448. user_id = await self.conn.get_user_id(msg)
  449. if user_id is None or not user_id['user_id']:
  450. return
  451. user_id = user_id['user_id']
  452. target_id = await self.conn.get_reply_id_reverse(msg)
  453. reason = ' '.join(msg.text.split(' ')[1:])
  454. dry_run = msg.text.split()[0].endswith('d')
  455. fwd_msg = None
  456. if self.warn_evidence_history_channel != 0:
  457. fwd_msg = (await self.app.forward_messages(
  458. self.warn_evidence_history_channel,
  459. self.target_group,
  460. target_id,
  461. True)).message_id
  462. if dry_run:
  463. await self.botapp.send_message(self.fudu_group, await self.generate_warn_message(user_id, reason),
  464. reply_to_message_id=msg.reply_to_message.message_id)
  465. else:
  466. warn_id = await self.conn.insert_new_warn(user_id, reason, fwd_msg)
  467. warn_msg = await self.botapp.send_message(self.target_group,
  468. await self.generate_warn_message(user_id, reason),
  469. reply_to_message_id=target_id)
  470. await self.botapp.send_message(
  471. self.fudu_group,
  472. _T('WARN SENT TO {}, Total warn {} time(s)').format(
  473. TextParser.parse_user_markdown(user_id),
  474. await self.conn.query_warn_by_user(user_id)
  475. ),
  476. parse_mode='markdown', reply_to_message_id=msg.message_id,
  477. reply_markup=InlineKeyboardMarkup(inline_keyboard=[
  478. [
  479. InlineKeyboardButton(
  480. text=_T('RECALL'),
  481. callback_data=f'warndel {warn_msg.message_id} {warn_id}'
  482. )
  483. ]
  484. ]))
  485. else: # Not reply message
  486. if msg.text == '/ban':
  487. await client.send_message(
  488. msg.chat.id, _T(
  489. 'Reply to the user you wish to restrict, '
  490. 'if you want to kick this user, please use the /kick command.'))
  491. elif msg.text == '/report':
  492. if self.join_group_verify_enable:
  493. _problem_total_count = \
  494. (await self.conn.query1('''SELECT COUNT(*) FROM "exam_user_session"'''))['count']
  495. result = []
  496. for problem_id in range(self.join_group_verify.problem_list.length):
  497. total_count, correct_count = await asyncio.gather(self.conn.query1(
  498. '''SELECT COUNT(*) FROM "exam_user_session" WHERE "problem_id" = $1''', problem_id),
  499. self.conn.query1(
  500. '''SELECT COUNT(*) FROM "exam_user_session"
  501. WHERE "problem_id" = $1 and "passed" = true''',
  502. problem_id))
  503. result.append(
  504. '`{}`: `{:.2f}`% / `{:.2f}`%'.format(problem_id,
  505. correct_count['count'] * 100 / total_count['count'],
  506. total_count['count'] * 100 / _problem_total_count))
  507. await msg.reply('Problem answer correct rate:\n{}'.format('\n'.join(result)))
  508. elif msg.text.startswith('/grant'):
  509. user_id = msg.text.split()[-1]
  510. await self.botapp.send_message(
  511. msg.chat.id,
  512. 'Do you want to grant user {}?'.format(
  513. TextParser.parse_user_markdown(user_id)),
  514. disable_notification=True,
  515. reply_to_message_id=msg.message_id,
  516. reply_markup=InlineKeyboardMarkup(
  517. [
  518. [InlineKeyboardButton('CHANGE INFO', f'grant {user_id} info'),
  519. InlineKeyboardButton('PIN', f'grant {user_id} pin')],
  520. [InlineKeyboardButton('RESTRICT', f'grant {user_id} restrict'),
  521. InlineKeyboardButton('DELETE', f'grant {user_id} delete')],
  522. [InlineKeyboardButton('confirm', f'grant {user_id} confirm'),
  523. InlineKeyboardButton('[DEBUG]Clear', f'grant {user_id} clear')],
  524. [InlineKeyboardButton('cancel', 'cancel')]
  525. ]))
  526. async def func_auth_process(self, _client: Client, msg: Message) -> None:
  527. if not self.auth_system.check_ex(msg.from_user.id):
  528. await msg.reply('Permission denied')
  529. return
  530. if msg.reply_to_message.from_user:
  531. if self.auth_system.check_ex(msg.reply_to_message.from_user.id):
  532. await msg.reply('Authorized')
  533. else:
  534. await self.botapp.send_message(
  535. msg.chat.id,
  536. 'Do you want to authorize {} ?\nThis confirmation message will expire after 20 seconds.'.format(
  537. TextParser.parse_user_markdown(msg.reply_to_message.from_user.id)
  538. ),
  539. reply_to_message_id=msg.message_id,
  540. parse_mode='markdown',
  541. reply_markup=InlineKeyboardMarkup(
  542. inline_keyboard=[
  543. [
  544. InlineKeyboardButton(text='Yes', callback_data='auth {} add'.format(
  545. msg.reply_to_message.from_user.id)),
  546. InlineKeyboardButton(text='No', callback_data='cancel')
  547. ]
  548. ]
  549. )
  550. )
  551. else:
  552. await msg.reply('Unexpected error.')
  553. async def cross_group_forward_request(self, msg: Message) -> None:
  554. kb = [
  555. [InlineKeyboardButton(text='Yes, I know what I\'m doing.', callback_data='fwd original')],
  556. [InlineKeyboardButton(text='Yes, but don\'t use forward.', callback_data='fwd text')],
  557. [InlineKeyboardButton(text='No, please don\'t.', callback_data='cancel d')]
  558. ]
  559. if msg.text is None: kb.pop(1)
  560. await self.botapp.send_message(
  561. msg.chat.id,
  562. '<b>Warning:</b> You are requesting forwarding an authorized user\'s '
  563. 'message to the main group, please confirm your action.',
  564. 'html',
  565. reply_to_message_id=msg.message_id,
  566. reply_markup=InlineKeyboardMarkup(inline_keyboard=kb)
  567. )
  568. del kb
  569. async def handle_new_member(self, client: Client, msg: Message) -> None:
  570. for new_user_id in (x.id for x in msg.new_chat_members):
  571. # Exam check goes here
  572. try:
  573. if not await self.join_group_verify.query_user_passed(new_user_id):
  574. await self.botapp.kick_chat_member(self.target_group, new_user_id)
  575. await self.botapp.send_message(self.fudu_group, 'Kicked challenge failure user {}'.format(
  576. TextParser.parse_user_markdown(new_user_id)), 'markdown')
  577. except BotController.ByPassVerify:
  578. pass
  579. except:
  580. logger.exception('Exception occurred!')
  581. if await self.conn.query_user_in_banlist(new_user_id):
  582. await self.botapp.kick_chat_member(msg.chat.id, new_user_id)
  583. await self.conn.insert(
  584. msg,
  585. await client.send_message(
  586. self.fudu_group,
  587. '`{}` invite `{}` joined the group'.format(
  588. TextParser.UserName(msg.from_user).full_name,
  589. '`,`'.join(
  590. TextParser.UserName(user).full_name for user in msg.new_chat_members
  591. )
  592. ),
  593. 'markdown'
  594. ) if msg.new_chat_members[0].id != msg.from_user.id else await client.send_message(
  595. self.fudu_group,
  596. '`{}` joined the group'.format(
  597. '`,`'.join(
  598. TextParser.UserName(user).full_name for user in msg.new_chat_members
  599. )
  600. ),
  601. 'markdown'
  602. )
  603. )
  604. async def handle_edit(self, client: Client, msg: Message) -> None:
  605. if msg.via_bot and msg.via_bot.id == 166035794:
  606. return
  607. target_id = await self.conn.get_id(msg.message_id)
  608. if target_id is None:
  609. logging.warning('Sleep 2 seconds for edit')
  610. await asyncio.sleep(2)
  611. target_id = await self.conn.get_id(msg.message_id)
  612. if target_id is None:
  613. return logger.error('Editing Failure: get_id return None')
  614. try:
  615. await (client.edit_message_text if msg.text else client.edit_message_caption)(
  616. self.fudu_group,
  617. target_id,
  618. TextParser(msg).get_full_message(),
  619. 'html'
  620. )
  621. except pyrogram.errors.MessageNotModified:
  622. logging.warning('Editing Failure: MessageNotModified')
  623. except:
  624. logger.exception('Exception occurred!')
  625. async def handle_sticker(self, client: Client, msg: Message) -> None:
  626. await self.conn.insert(
  627. msg,
  628. await client.send_message(
  629. self.fudu_group,
  630. f'{TextParser(msg).get_full_message()} {msg.sticker.emoji} sticker',
  631. parse_mode='html',
  632. disable_web_page_preview=True,
  633. disable_notification=True,
  634. reply_to_message_id=await self.conn.get_reply_id(msg),
  635. )
  636. )
  637. async def _get_reply_id(self, msg: Message, reverse: bool = False) -> Optional[int]:
  638. if msg.reply_to_message is None:
  639. return None
  640. return await self.conn.get_id(msg.reply_to_message.message_id, reverse)
  641. async def send_media(self, client: Client, msg: Message, send_to: int, caption: str, reverse: bool = False) -> None:
  642. msg_type = self.get_file_type(msg)
  643. while True:
  644. try:
  645. _msg = await client.send_cached_media(
  646. send_to,
  647. self.get_file_id(msg, msg_type),
  648. # self.get_file_ref(msg, msg_type),
  649. caption=caption,
  650. parse_mode='html',
  651. disable_notification=True,
  652. reply_to_message_id=await self._get_reply_id(msg, reverse)
  653. )
  654. if reverse:
  655. await self.conn.insert_ex(_msg.message_id, int(msg.caption.split()[1]))
  656. else:
  657. await self.conn.insert(msg, _msg)
  658. break
  659. except pyrogram.errors.FloodWait as e:
  660. logger.warning('Pause %d seconds because 420 flood wait', e.x)
  661. await asyncio.sleep(e.x)
  662. except:
  663. logger.exception('Exception occurred!')
  664. break
  665. async def handle_all_media(self, client: Client, msg: Message) -> None:
  666. await self.send_media(client, msg, self.fudu_group, TextParser(msg).get_full_message())
  667. async def handle_dice(self, client: Client, msg: Message) -> None:
  668. if msg.dice:
  669. await self.conn.insert(
  670. msg,
  671. await client.send_message(
  672. self.fudu_group,
  673. '{} {} dice[{}]'.format(
  674. TextParser(msg).get_full_message(),
  675. msg.dice.emoji,
  676. msg.dice.value
  677. ),
  678. 'html',
  679. disable_web_page_preview=True,
  680. disable_notification=True,
  681. reply_to_message_id=await self.conn.get_reply_id(msg)
  682. )
  683. )
  684. else:
  685. raise ContinuePropagation()
  686. @staticmethod
  687. def get_file_id(msg: Message, _type: str) -> str:
  688. return getattr(msg, _type).file_id
  689. @staticmethod
  690. def get_file_ref(msg: Message, _type: str) -> str:
  691. return getattr(msg, _type).file_ref
  692. @staticmethod
  693. def get_file_type(msg: Message) -> str:
  694. return 'photo' if msg.photo else \
  695. 'video' if msg.video else \
  696. 'animation' if msg.animation else \
  697. 'sticker' if msg.sticker else \
  698. 'voice' if msg.voice else \
  699. 'document' if msg.document else \
  700. 'text' if msg.text else 'error'
  701. async def handle_speak(self, client: Client, msg: Message) -> None:
  702. if msg.text.startswith('/') and re.match(r'^/\w+(@\w*)?$', msg.text):
  703. return
  704. await self.conn.insert(
  705. msg,
  706. await client.send_message(
  707. self.fudu_group,
  708. TextParser(msg).get_full_message(),
  709. 'html',
  710. disable_web_page_preview=not msg.web_page,
  711. disable_notification=True,
  712. reply_to_message_id=await self.conn.get_reply_id(msg)
  713. )
  714. )
  715. async def handle_bot_send_media(self, client: Client, msg: Message) -> None:
  716. def parse_caption(caption: str) -> str:
  717. obj = caption.split(maxsplit=3)
  718. return '' if len(obj) < 3 else obj[-1]
  719. await self.send_media(client, msg, self.target_group, parse_caption(TextParser(msg).split_offset()),
  720. True)
  721. async def handle_incoming(self, client: Client, msg: Message) -> None:
  722. # NOTE: Remove debug code and other handle code from offical version
  723. await client.send(
  724. raw.functions.channels.ReadHistory(channel=await client.resolve_peer(msg.chat.id), max_id=msg.message_id))
  725. if msg.reply_to_message:
  726. await client.send(raw.functions.messages.ReadMentions(peer=await client.resolve_peer(msg.chat.id)))
  727. if msg.text == '/auth' and msg.reply_to_message:
  728. return await self.func_auth_process(client, msg)
  729. if not self.auth_system.check_ex(msg.from_user.id):
  730. return
  731. if msg.text and re.match(
  732. r'^/(bot (on|off)|del|get|fw|ban( ([1-9]\d*)[smhd]|f)?|kick( confirm| -?\d+)?|status|b?o(n|ff)|join|'
  733. r'promote( \d+)?|set [a-zA-Z]|pina?|su(do)?|title .*|warnd? .*|grant \d+|report)$',
  734. msg.text
  735. ):
  736. return await self.process_incoming_command(client, msg)
  737. if msg.text and msg.text.startswith('/') and re.match(r'^/\w+(@\w*)?$', msg.text):
  738. return
  739. if self.auth_system.check_muted(msg.from_user.id) or (msg.text and msg.text.startswith('//')) or (
  740. msg.caption and msg.caption.startswith('//')):
  741. return
  742. if msg.forward_from or msg.forward_from_chat or msg.forward_sender_name:
  743. if msg.forward_from:
  744. if msg.forward_from.is_self:
  745. return
  746. elif self.auth_system.check_ex(msg.forward_from.id):
  747. return await self.cross_group_forward_request(msg)
  748. await self.conn.insert_ex(
  749. (await self.botapp.forward_messages(self.target_group, self.fudu_group, msg.message_id)).message_id,
  750. msg.message_id)
  751. elif msg.text and (
  752. not msg.edit_date or (msg.edit_date and await self.conn.get_id(msg.message_id, True) is None)):
  753. await self.conn.insert_ex(
  754. (await self.botapp.send_message(
  755. self.target_group,
  756. TextParser(msg).split_offset(),
  757. 'html',
  758. disable_web_page_preview=not msg.web_page,
  759. reply_to_message_id=await self.conn.get_reply_id_reverse(msg),
  760. )).message_id, msg.message_id
  761. )
  762. elif msg.photo or msg.video or msg.animation or msg.document:
  763. _type = self.get_file_type(msg)
  764. await (await client.send_cached_media(
  765. msg.chat.id,
  766. self.get_file_id(msg, _type),
  767. # self.get_file_ref(msg, _type),
  768. f'/SendMedia {msg.message_id} {TextParser(msg).split_offset()}',
  769. parse_mode='html',
  770. disable_notification=True,
  771. reply_to_message_id=msg.reply_to_message.message_id if msg.reply_to_message else None
  772. )).delete()
  773. elif msg.edit_date:
  774. try:
  775. await (self.botapp.edit_message_text if msg.text else self.botapp.edit_message_caption)(
  776. self.target_group,
  777. await self.conn.get_id(msg.message_id, True),
  778. TextParser(msg).split_offset(),
  779. parse_mode='html',
  780. disable_web_page_preview=not msg.web_page
  781. )
  782. except:
  783. logger.exception('Exception occurred!')
  784. elif msg.sticker:
  785. await self.conn.insert_ex(
  786. (await self.botapp.send_sticker(self.target_group, msg.sticker.file_id,
  787. reply_to_message_id=await self.conn.get_reply_id_reverse(
  788. msg))).message_id,
  789. msg.message_id
  790. )
  791. async def handle_callback(self, client: Client, msg: CallbackQuery) -> None:
  792. if msg.message.chat.id < 0 and msg.message.chat.id != self.fudu_group: return
  793. args = msg.data.split()
  794. try:
  795. if msg.data.startswith('cancel') or msg.data == 'rm':
  796. if msg.data.endswith('d'):
  797. await msg.message.delete()
  798. else:
  799. await msg.edit_message_reply_markup()
  800. if self.join_group_verify_enable and \
  801. self.join_group_verify is not None and \
  802. await self.join_group_verify.click_to_join(client, msg):
  803. return
  804. if msg.data.startswith('res'):
  805. if time.time() - msg.message.date > 20:
  806. raise OperationTimeoutError()
  807. _, dur, _type, _user_id = args
  808. if await client.restrict_chat_member(
  809. self.target_group,
  810. int(_user_id),
  811. {
  812. 'write': ChatPermissions(can_send_messages=True),
  813. 'media': ChatPermissions(can_send_media_messages=True),
  814. 'stickers': ChatPermissions(can_send_stickers=True),
  815. 'link': ChatPermissions(can_add_web_page_previews=True),
  816. 'read': ChatPermissions()
  817. }.get(_type),
  818. int(time.time()) + int(dur)
  819. ):
  820. await msg.answer('The user is restricted successfully.')
  821. await client.edit_message_text(
  822. msg.message.chat.id,
  823. msg.message.message_id,
  824. 'Restrictions applied to {} Duration: {}'.format(
  825. TextParser.parse_user_markdown(_user_id),
  826. f'{dur}s' if int(dur) else 'Forever'),
  827. parse_mode='markdown',
  828. reply_markup=InlineKeyboardMarkup([[InlineKeyboardButton(
  829. text='UNBAN', callback_data=f'unban {_user_id}')]])
  830. )
  831. elif msg.data.startswith('unban'):
  832. if await client.restrict_chat_member(self.target_group, int(args[-1]), ChatPermissions(
  833. can_send_messages=True,
  834. can_send_stickers=True,
  835. can_send_polls=True,
  836. can_add_web_page_previews=True,
  837. can_send_media_messages=True,
  838. can_send_animations=True,
  839. can_pin_messages=True,
  840. can_invite_users=True,
  841. can_change_info=True
  842. )):
  843. await asyncio.gather(msg.answer('Unban successfully'),
  844. client.edit_message_reply_markup(msg.message.chat.id, msg.message.message_id))
  845. elif msg.data.startswith('auth'):
  846. if time.time() - msg.message.date > 20:
  847. raise OperationTimeoutError()
  848. await self.auth_system.add_user(args[1])
  849. await asyncio.gather(msg.answer(f'{args[1]} added to the authorized group'),
  850. msg.message.edit(f'{args[1]} added to the authorized group'))
  851. elif msg.data.startswith('fwd'):
  852. if time.time() - msg.message.date > 30:
  853. raise OperationTimeoutError()
  854. if 'original' in msg.data:
  855. # Process original forward
  856. await self.conn.insert_ex(
  857. (await client.forward_messages(
  858. self.target_group,
  859. msg.message.chat.id,
  860. msg.message.reply_to_message.message_id)
  861. ).message_id,
  862. msg.message.reply_to_message.message_id
  863. )
  864. else:
  865. await self.conn.insert_ex((await client.send_message(self.target_group, TextParser(
  866. msg.message.reply_to_message).split_offset(), 'html')).message_id,
  867. msg.message.reply_to_message.message_id)
  868. await asyncio.gather(msg.answer('Forward successfully'), msg.message.delete())
  869. elif msg.data.startswith('kick'):
  870. # _TODO: Process parallel request (deprecated)
  871. if not msg.data.startswith('kickc') and msg.from_user.id != int(args[-2]):
  872. raise OperatorError()
  873. if 'true' not in msg.data:
  874. if not msg.data.startswith('kickc') and time.time() - msg.message.date > 15:
  875. raise OperationTimeoutError()
  876. client_args = [
  877. msg.message.chat.id,
  878. msg.message.message_id,
  879. 'Press the button again to kick {}\n'
  880. 'This confirmation message will expire after 10 seconds.'.format(
  881. TextParser.parse_user_markdown(args[-1])
  882. ),
  883. ]
  884. if msg.data.startswith('kickc'):
  885. client_args.pop(1)
  886. r = list(client_args)
  887. r.insert(1, msg.from_user.id)
  888. msg.data = ' '.join(map(str, r))
  889. del r
  890. kwargs = {
  891. 'parse_mode': 'markdown',
  892. 'reply_markup': InlineKeyboardMarkup(inline_keyboard=[
  893. [InlineKeyboardButton(text='Yes, please.',
  894. callback_data=' '.join(('kick true', ' '.join(map(str, args[1:])))))],
  895. [InlineKeyboardButton(text='Cancel', callback_data='cancel')]
  896. ])
  897. }
  898. await (client.send_message if msg.data.startswith('kickc') else client.edit_message_text)(
  899. *client_args, **kwargs)
  900. await msg.answer(
  901. f'Please press again to make sure. Do you really want to kick {args[-1]} ?', True)
  902. else:
  903. if msg.message.edit_date:
  904. if time.time() - msg.message.edit_date > 10:
  905. raise OperationTimeoutError()
  906. else:
  907. if time.time() - msg.message.date > 10:
  908. raise OperationTimeoutError()
  909. await client.kick_chat_member(self.target_group, int(args[-1]))
  910. await asyncio.gather(msg.answer(f'Kicked {args[-1]}'),
  911. msg.message.edit(f'Kicked {TextParser.parse_user_markdown(args[-1])}'))
  912. elif msg.data.startswith('promote'):
  913. if not msg.data.endswith('undo'):
  914. if time.time() - msg.message.date > 10:
  915. raise OperationTimeoutError()
  916. await self.botapp.promote_chat_member(
  917. self.target_group,
  918. int(args[1]),
  919. True,
  920. can_delete_messages=True,
  921. can_restrict_members=True,
  922. can_invite_users=True,
  923. can_pin_messages=True,
  924. can_promote_members=True,
  925. )
  926. await msg.answer('Promote successfully')
  927. await msg.message.edit(
  928. f'Promoted {TextParser.parse_user_markdown(int(args[1]))}',
  929. parse_mode='markdown',
  930. reply_markup=InlineKeyboardMarkup(inline_keyboard=[
  931. [InlineKeyboardButton(text='UNDO', callback_data=' '.join((msg.data, 'undo')))],
  932. [InlineKeyboardButton(text='remove button', callback_data='rm')]])
  933. )
  934. else:
  935. await self.botapp.promote_chat_member(
  936. self.target_group, int(args[1]),
  937. False,
  938. can_delete_messages=False,
  939. can_invite_users=False,
  940. can_restrict_members=False
  941. )
  942. await asyncio.gather(
  943. msg.answer('Undo Promote successfully'),
  944. msg.message.edit(
  945. f'Undo promoted {TextParser.parse_user_markdown(int(args[1]))}',
  946. parse_mode='markdown')
  947. )
  948. elif msg.data.startswith('grant'):
  949. _redis_key_str = f'promote_{msg.message.chat.id}_{args[1]}'
  950. if args[2] == 'confirm':
  951. select_privileges = await self._redis.get(_redis_key_str)
  952. await self._redis.delete(_redis_key_str)
  953. if select_privileges is None:
  954. raise OperationTimeoutError()
  955. grant_args = {}
  956. for x in map(lambda x: x.strip(), select_privileges.decode().split(',')):
  957. if x == 'info':
  958. grant_args.update({'can_change_info': True})
  959. elif x == 'delete':
  960. grant_args.update({'can_delete_messages': True})
  961. elif x == 'restrict':
  962. grant_args.update({'can_restrict_members': True})
  963. elif x == 'pin':
  964. grant_args.update({'can_pin_messages': True})
  965. await self.botapp.promote_chat_member(self.target_group, int(args[1]), **grant_args)
  966. await msg.message.edit('Undo grant privileges', reply_markup=InlineKeyboardMarkup(
  967. [[InlineKeyboardButton('UNDO', f'grant {args[1]} undo')]]))
  968. await msg.answer()
  969. elif args[2] == 'undo':
  970. await self.botapp.promote_chat_member(self.target_group, int(args[1]), False,
  971. can_delete_messages=False, can_restrict_members=False)
  972. await msg.message.edit_reply_markup()
  973. await msg.answer()
  974. elif args[2] == 'clear':
  975. self._redis.delete(_redis_key_str)
  976. await msg.answer()
  977. else:
  978. if time.time() - msg.message.date > 40:
  979. raise OperationTimeoutError()
  980. # original_msg = msg.message.text.splitlines()[0]
  981. select_privileges = self._redis.get(_redis_key_str)
  982. if select_privileges is None:
  983. select_privileges = [args[2]]
  984. self._redis.set(_redis_key_str, select_privileges[0])
  985. self._redis.expire(_redis_key_str, 60)
  986. else:
  987. select_privileges = list(map(lambda x: x.strip(), select_privileges.decode().split(',')))
  988. if args[2] in select_privileges:
  989. if len(select_privileges) == 1:
  990. return await msg.answer('You should choose at least one privilege.', True)
  991. select_privileges.remove(args[2])
  992. else:
  993. select_privileges.append(args[2])
  994. await self._redis.set(_redis_key_str, ','.join(select_privileges))
  995. await msg.message.edit(
  996. 'Do you want to grant user {}?\n\nSelect privileges:\n{}'.format(
  997. TextParser.parse_user_markdown(args[1]),
  998. '\n'.join(select_privileges)),
  999. reply_markup=msg.message.reply_markup)
  1000. # return await msg.answer(f'Promoted {args[2]} permission')
  1001. elif msg.data == 'unpin':
  1002. await self.botapp.unpin_chat_message(self.target_group)
  1003. await asyncio.gather(msg.message.edit_reply_markup(),
  1004. msg.answer())
  1005. elif msg.data.startswith('warndel'):
  1006. await self.botapp.delete_messages(self.target_group, int(args[1]))
  1007. await self.conn.delete_warn_by_id(int(args[2]))
  1008. await asyncio.gather(msg.message.edit_reply_markup(),
  1009. msg.answer())
  1010. except OperationTimeoutError:
  1011. await asyncio.gather(msg.answer('Confirmation time out'),
  1012. client.edit_message_reply_markup(msg.message.chat.id, msg.message.message_id))
  1013. except OperatorError:
  1014. await msg.answer(f'The operator should be {args[-2]}.', True)
  1015. except:
  1016. await self.app.send_message(config.getint('custom_service', 'help_group'),
  1017. traceback.format_exc().splitlines()[-1])
  1018. logger.exception('Exception occurred!')
  1019. async def main():
  1020. bot = await BotController.create()
  1021. await bot.start()
  1022. await bot.idle()
  1023. await bot.stop()
  1024. if __name__ == '__main__':
  1025. coloredlogs.install(logging.DEBUG,
  1026. fmt='%(asctime)s,%(msecs)03d - %(levelname)s - %(funcName)s - %(lineno)d - %(message)s')
  1027. if '--debug-pyrogram' in sys.argv:
  1028. # logging.getLogger('pyrogram').setLevel(logging.INFO)
  1029. pyrogram_file_handler = logging.FileHandler('pyrogram.log')
  1030. pyrogram_file_handler.setFormatter(
  1031. coloredlogs.ColoredFormatter(
  1032. '%(asctime)s,%(msecs)03d - %(levelname)s - %(funcName)s - %(lineno)d - %(message)s'))
  1033. logging.getLogger('pyrogram').addHandler(pyrogram_file_handler)
  1034. else:
  1035. logging.getLogger("pyrogram").setLevel(logging.WARNING)
  1036. file_handler = logging.FileHandler('log.log')
  1037. file_handler.setLevel(logging.DEBUG)
  1038. file_handler.setFormatter(coloredlogs.ColoredFormatter(
  1039. '%(asctime)s,%(msecs)03d - %(levelname)s - %(funcName)s - %(lineno)d - %(message)s'))
  1040. logging.getLogger('telegram-repeater').addHandler(file_handler)
  1041. loop = asyncio.get_event_loop()
  1042. loop.run_until_complete(main())
  1043. loop.close()