Keeps pedophiles and CP spammers away from telegram groups by checking if member or media hash is blacklisted.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. from magic import Magic
  2. from os import remove,linesep
  3. import logging
  4. from imagehash import average_hash
  5. from PIL import Image
  6. from telegram.error import (
  7. BadRequest,
  8. TimedOut,
  9. Unauthorized,
  10. InvalidToken
  11. )
  12. from telegram import (
  13. ReplyKeyboardMarkup,
  14. ReplyKeyboardRemove,
  15. Update, ReplyKeyboardMarkup,
  16. ReplyKeyboardRemove
  17. )
  18. from telegram.ext import (
  19. Updater,
  20. CommandHandler,
  21. MessageHandler,
  22. Filters,
  23. ConversationHandler
  24. )
  25. from tools import (
  26. GREETING_MESSAGE,
  27. BOT_ADDED_MESSAGE,
  28. dump_json,
  29. load_json
  30. )
  31. # Will log only banned users.
  32. logging.basicConfig(
  33. level=logging.WARN,
  34. format='%(asctime)s - %(message)s',
  35. handlers=[
  36. logging.FileHandler("debug.log"),
  37. logging.StreamHandler()
  38. ]
  39. )
  40. debug =logging.getLogger("AntiPedo")
  41. filter_list = load_json("filter.json")
  42. chan_list = load_json("chats.json")
  43. meta =load_json("meta.json")
  44. admins = meta["admins"]
  45. TOKEN = meta["token"]
  46. REPORT_CH = meta["report_ch"]
  47. updater = Updater(TOKEN)
  48. dispatcher = updater.dispatcher
  49. filetype = Magic(mime=True)
  50. def checkNewMember(update, context):
  51. chat = update.effective_chat
  52. blacklist = load_json("blacklist.json")
  53. for member in update.message.new_chat_members:
  54. if member.id == updater.bot.id:
  55. trackPublicChats(update,context)
  56. try:update.message.reply_text(BOT_ADDED_MESSAGE,quote=False)
  57. except Unauthorized as error: debug.warning(f"[ERROR] {repr(error)} - {str(error)}")
  58. return
  59. if not str(member.id) in blacklist:
  60. continue
  61. meta = load_json("meta.json")
  62. meta["banned"] = meta["banned"]+1
  63. dump_json(meta,"meta.json")
  64. mods=0
  65. debug.warning(f"Black-listed user {member.username or member.full_name} joined"
  66. f" to {'@'+chat.username if chat.username else chat.title} CID:{chat.id}.")
  67. reason = blacklist[str(member.id)]
  68. if reason.replace("+","").isnumeric():
  69. reason = "\n".join([ blacklist["CODES"][i] for i in reason.split("+") ])
  70. if updater.bot.getChatMember( chat.id,updater.bot.id ).can_restrict_members:
  71. updater.bot.kick_chat_member(chat.id, member.id)
  72. action='Automatically banned.'
  73. else:
  74. # If the bot doesn't have admin previleges, send a warning message to the admins of group instead.
  75. action='Reported to group moderators.'
  76. for op in [ i.user for i in update.effective_chat.get_administrators() if not i.user.is_bot]:
  77. try:
  78. op.send_message(
  79. f"🚨 KNOWN PEDO ALERT @ {str(update.effective_chat.title).upper()} 🚨\n"
  80. f"username: {member.username}\n"
  81. f"fullname: {member.full_name}\n"
  82. f"reason: {reason}")
  83. except Exception as error:
  84. if not "initiate" in str(error):
  85. debug.warning(f"[ERROR] {repr(error)} - {str(error)}")
  86. else:
  87. mods+=1
  88. debug.warning(f"[ACTION] {action.replace(' mod',f' {mods} mod')} ID:{member.id} \n{' '*26}Guilt: {reason.replace(linesep,' ')}")
  89. try:update.message.reply_text(
  90. f'Known pedo <a href="tg://user?id={member.id}">{member.full_name}</a> joined to group. {action}\n\n'
  91. f'<b>Reason:</b> {reason}', quote=True, parse_mode="html")
  92. except BadRequest as error: debug.warning(f"[ERROR] {repr(error)} - {str(error)}")
  93. def updateBlacklist(update,context):
  94. user = update.effective_user
  95. if not user.id in admins:
  96. debug.warning(f"Non-operator user {user.username or user.full_name} tried to use /add. ID:{user.id}")
  97. update.message.reply_text("You are not a bot operator.")
  98. return ConversationHandler.END
  99. try:
  100. uid = update.message.text.split(" ")[1]
  101. int(uid)
  102. except (ValueError,IndexError):
  103. update.message.reply_text("Incorrect User ID.")
  104. return ConversationHandler.END
  105. blacklist = load_json("blacklist.json")
  106. if uid in blacklist:
  107. update.message.reply_text("This user is already black-listed.")
  108. return ConversationHandler.END
  109. context.user_data["uids"]=[uid]
  110. blacklist = load_json("blacklist.json")
  111. update.message.reply_text("Now please send me either a reason number from below or send a new reason message. Send 'cancel' to abort.")
  112. update.message.reply_text("\n".join([ f"{i}. {t}" for i,t in blacklist["CODES"].items()]))
  113. return 1
  114. def setReason(update,context):
  115. reason = update.message.text
  116. uids = context.user_data["uids"]
  117. blacklist = load_json("blacklist.json")
  118. for uid in uids:
  119. blacklist[uid]=reason
  120. dump_json(blacklist,"blacklist.json")
  121. update.message.reply_text("Success.")
  122. return ConversationHandler.END
  123. add_blacklist_convo = lambda command,func:ConversationHandler(
  124. entry_points=[CommandHandler(command, func)],
  125. states={
  126. 1:[MessageHandler(Filters.text & ~Filters.regex("^[Cc]ancel\.*$"),setReason)],
  127. -2:[MessageHandler(Filters.all, Report.timeout)]
  128. },
  129. fallbacks=[MessageHandler(Filters.regex("^[Cc]ancel\.*$"), Report.cancel)],
  130. conversation_timeout=120
  131. )
  132. def updatePicBlacklist(update,context):
  133. if not update.message.from_user.id in admins:
  134. return update.message.reply_text("You are not a bot operator.")
  135. m = update.message.reply_to_message
  136. if not m:
  137. return update.message.reply_text("Reply to a media.")
  138. for method in [m.video,m.animation,m.photo]:
  139. if not method:
  140. continue
  141. elif "thumb" in dir(method) and method.thumb:
  142. file = method.thumb
  143. elif m.photo:
  144. file = m.photo[0]
  145. else:
  146. return
  147. break
  148. path = file.get_file().download(f"/tmp/{file.file_unique_id}")
  149. phash = str(average_hash(Image.open(path)))
  150. remove(path)
  151. if not phash in filter_list:
  152. filter_list.append(phash)
  153. dump_json(filter_list,"filter.json")
  154. update.message.reply_text("Sucessfully added.")
  155. else:
  156. update.message.reply_text("This media is already filtered.")
  157. def batchBlacklist(update,context):
  158. if not update.message.from_user.id in admins:
  159. update.message.reply_text("You are not a bot operator.")
  160. return ConversationHandler.END
  161. message = update.message.reply_to_message
  162. if not message:
  163. update.message.reply_text("Usage: reply to an ID list file or a message containing list.")
  164. return ConversationHandler.END
  165. if message.document:
  166. # Check if list file is valid.
  167. message.document.get_file().download("batch_list")
  168. if filetype.from_file("batch_list") != "text/plain":
  169. remove("batch_list")
  170. update.message.reply_text("File type unsupported.")
  171. return ConversationHandler.END
  172. with open("batch_list") as ro:
  173. batch_list_raw = ro.read().replace(" ","")
  174. remove("batch_list")
  175. elif message.text:
  176. batch_list_raw = message.text.replace(" ","")
  177. else:
  178. update.message.reply_text("Unsupported batch adding method.")
  179. return ConversationHandler.END
  180. if False in [i.isdigit() for i in batch_list_raw if not '\n' in i]:
  181. update.message.reply_text("Unsupported characters in list, you should send the IDs in newlines as integers with no comma or dots.")
  182. return ConversationHandler.END
  183. batch_list = [ int(i) for i in batch_list_raw.split("\n") if i ]
  184. # Finally add new IDs to the black list.
  185. blacklist = load_json("blacklist.json")
  186. new,uids = 0,[]
  187. for i in batch_list:
  188. if not str(i) in blacklist:
  189. new += 1
  190. uids.append(i)
  191. text = f"{new} new IDs selected."
  192. update.message.reply_text(f"{text} Now please send me either a reason number from below or send a new reason message. Send 'cancel' to abort.")
  193. update.message.reply_text("\n".join([ f"{i}. {t}" for i,t in blacklist["CODES"].items()]))
  194. debug.warning(text)
  195. context.user_data["uids"]=uids
  196. return 1
  197. def metaInformation(update,context):
  198. blacklist = load_json("blacklist.json")
  199. meta = load_json("meta.json")
  200. filtre = load_json("filter.json")
  201. update.message.reply_text(
  202. f"Currently {len(blacklist)-1} pedos on blacklist. "
  203. f"{meta['banned']} pedophiles were banned "
  204. f"and {meta['reported']} reports received. "
  205. f"Protecting {len(chan_list)} groups. {len(filtre)} CP media on filter so far."
  206. )
  207. def giveOP(update,context):
  208. if not update.message.from_user.id in admins:
  209. debug.warning(f"[DETECT] Black-listed user {user.username or user.full_name} tried to /OP. ID:{user.id}")
  210. return update.message.reply_text("You are not a bot operator.")
  211. try:
  212. uid = update.message.text.split("/op ")[1]
  213. uid = int(uid)
  214. except (ValueError,IndexError):
  215. return update.message.reply_text("Incorrect User ID.")
  216. if uid in admins:
  217. return update.message.reply_text("This user is already bot operator.")
  218. admins.append(uid)
  219. meta = load_json("meta.json")
  220. meta["admins"]=admins
  221. dump_json(meta,"meta.json")
  222. return update.message.reply_text("Successfully added new bot operator.")
  223. class Report:
  224. def start(update,context):
  225. if "private" != update.effective_chat.type:
  226. try:update.message.reply_text("You must /report trough private chat.")
  227. except Exception as error: debug.warning(f"[ERROR] {repr(error)} - {str(error)}")
  228. return ConversationHandler.END
  229. context.user_data["messages"] = []
  230. update.message.reply_text(
  231. "Send me up to 5 messages and then press \"Forward\" button.",
  232. reply_markup=ReplyKeyboardMarkup([['Forward 🟢','Cancel 🔴']], one_time_keyboard=True)
  233. )
  234. return 1
  235. def cancel(update,context):
  236. update.message.reply_text("Cancelled.",reply_markup=ReplyKeyboardRemove())
  237. return ConversationHandler.END
  238. def handle(update,context):
  239. if len(context.user_data["messages"]) < 5:
  240. context.user_data["messages"].append( update.message )
  241. return 1
  242. update.message.reply_text("You can send maximum 5 messages.")
  243. def send(update,context):
  244. if not context.user_data["messages"]:
  245. update.message.reply_text(
  246. "No messages were sent.",
  247. reply_markup=ReplyKeyboardRemove()
  248. )
  249. return ConversationHandler.END
  250. user = update.effective_user
  251. updater.bot.sendMessage(REPORT_CH,
  252. f"🚨🚨 NEW PEDO REPORT 🚨🚨\n"
  253. f"<code>from User ID: {user.id}</code>\n"
  254. f"<code>from UserName: </code>@{user.username}",
  255. parse_mode="html")
  256. for message in context.user_data["messages"]:
  257. message.forward(REPORT_CH)
  258. update.message.reply_text(
  259. "Your report has been sent.",
  260. reply_markup=ReplyKeyboardRemove()
  261. )
  262. meta = load_json("meta.json")
  263. meta["reported"]=meta["reported"]+1
  264. dump_json(meta,"meta.json")
  265. return ConversationHandler.END
  266. def timeout(update,context):
  267. update.message.reply_text("Timeout.",reply_markup=ReplyKeyboardRemove())
  268. def reply(update,context):
  269. if not update.effective_user.id in admins:
  270. return
  271. forwarded = update.message.reply_to_message
  272. if not forwarded or not "ID:" in forwarded.text:
  273. return update.message.reply_text("Reply to the bot's alert message.")
  274. try:
  275. user_id = int(forwarded.text.split("ID: ")[1].split("\n")[0])
  276. except Exception as error:
  277. debug.warning(f"[ERROR] {repr(error)} - {str(error)}")
  278. return
  279. updater.bot.send_message(
  280. user_id,
  281. update.message.text.split("[FORWARD]\n")[1]
  282. )
  283. return update.message.reply_text("Message sent.")
  284. reporter = ConversationHandler(
  285. entry_points=[CommandHandler('report', Report.start)],
  286. states={
  287. 1: [
  288. MessageHandler(Filters.regex("^Forward 🟢$"), Report.send),
  289. MessageHandler(Filters.regex("^Cancel 🔴$") | Filters.command , Report.cancel),
  290. MessageHandler(Filters.text | Filters.photo, Report.handle)],
  291. -2: [MessageHandler(Filters.all, Report.timeout) ]
  292. },
  293. fallbacks=[MessageHandler(Filters.regex("^Cancel 🔴$"), Report.cancel)],
  294. conversation_timeout=120
  295. )
  296. def start(update,context):
  297. if "private" != update.effective_chat.type:
  298. return
  299. debug.warning(f"{update.effective_user.id} started bot.")
  300. update.message.reply_text(GREETING_MESSAGE,parse_mode="html")
  301. def checkMedia(update,context):
  302. if update.effective_user and update.effective_user.id in admins:
  303. # Skip bot operators to avoid getting them banned while adding media to blacklist.
  304. return
  305. m = update.effective_message
  306. for method in [m.video,m.animation,m.photo]:
  307. if not method:
  308. continue
  309. elif "thumb" in dir(method) and method.thumb:
  310. file = method.thumb
  311. elif m.photo:
  312. file = m.photo[0]
  313. else:
  314. return
  315. break
  316. try:
  317. path = file.get_file().download(f"/tmp/{file.file_unique_id}")
  318. except (TimedOut,InvalidToken):
  319. return
  320. phash = str(average_hash( Image.open(path) ))
  321. remove(path)
  322. if not phash in filter_list:
  323. return
  324. # Add this user to blacklist
  325. chat = update.effective_chat
  326. member = update.effective_user
  327. debug.warning(f"[DETECT] User {member.username or member.full_name} posted CP "
  328. f"at {'@'+chat.username if chat.username else chat.title}.")
  329. blacklist = load_json("blacklist.json")
  330. if not str(member.id) in blacklist:
  331. blacklist[str(member.id)]="3"
  332. dump_json(blacklist,"blacklist.json")
  333. meta = load_json("meta.json")
  334. meta["banned"] = meta["banned"]+1
  335. dump_json(meta,"meta.json")
  336. # Delete his message and ban him
  337. bot = updater.bot.getChatMember( m.chat_id,updater.bot.id )
  338. text = "CP image detected and informed moderators. Can't delete since no permissions."
  339. if bot.can_delete_messages:
  340. try:
  341. m.delete()
  342. except Unauthorized as error:
  343. debug.warning(f"[ERROR] {repr(error)} - {str(error)}")
  344. else:
  345. text = "Automatically deleted CP image and informed moderators."
  346. if bot.can_restrict_members:
  347. updater.bot.kick_chat_member(m.chat_id, member.id)
  348. text = "Automatically deleted CP image and banned the CP poster."
  349. else:
  350. # If the bot doesn't have admin previleges, send a warning message to the admins of group instead.
  351. for op in [ i.user for i in update.effective_chat.get_administrators() if not i.user.is_bot]:
  352. try:
  353. op.send_message(
  354. f"🚨 CP SPAM ALERT @ {str(update.effective_chat.title).upper()} 🚨\n"
  355. f"username: {member.username}\n"
  356. f"fullname: {member.full_name}")
  357. except Exception as error:
  358. if not "initiate" in str(error):
  359. debug.warning(f"[ERROR] {repr(error)} - {str(error)}")
  360. debug.warning(f"[ACTION] {text} ID:{member.id}")
  361. return update.message.bot.send_message(chat.id,text)
  362. def trackPublicChats(update,context):
  363. chat = update.effective_chat
  364. if chat.type=="private" or str(chat.id) in chan_list:
  365. return
  366. debug.warning(f"Bot was added to {chat.title}")
  367. try:
  368. chan_list[chat.id] = {
  369. "full_name": chat.full_name or chat.title,
  370. "username" : chat.username or chat.invite_link,
  371. "type" : chat.type,
  372. "admin" : updater.bot.getChatMember( chat.id,updater.bot.id ).can_restrict_members,
  373. "members" : chat.get_members_count()
  374. }
  375. except Unauthorized:
  376. return
  377. dump_json(chan_list,"chats.json")
  378. dispatcher.add_handler(reporter)
  379. dispatcher.add_handler(add_blacklist_convo("add",updateBlacklist))
  380. dispatcher.add_handler(add_blacklist_convo("batch_add",batchBlacklist))
  381. dispatcher.add_handler(MessageHandler(Filters.regex("^\[FORWARD\]\\n"), Report.reply))
  382. dispatcher.add_handler(MessageHandler(Filters.status_update.new_chat_members, checkNewMember))
  383. dispatcher.add_handler(MessageHandler(Filters.photo | Filters.animation | Filters.video, checkMedia))
  384. dispatcher.add_handler(CommandHandler("add_filter", updatePicBlacklist))
  385. dispatcher.add_handler(CommandHandler("meta", metaInformation))
  386. dispatcher.add_handler(CommandHandler("op", giveOP))
  387. dispatcher.add_handler(CommandHandler(["help","start"] , start))
  388. updater.start_polling()