Keeps pedophiles and CP spammers away from telegram groups by checking if member or media hash is blacklisted.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

antipedo.py 16KB

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