def prepare_user_message(user, msg_score, is_media): if user.isInCooldown(): return rp.Reply(rp.types.ERR_COOLDOWN, until=user.cooldownUntil) if is_media and user.rank < RANKS.mod and media_limit_period is not None: if (datetime.now() - user.joined) < media_limit_period: return rp.Reply(rp.types.ERR_MEDIA_LIMIT) ok = spam_scores.increaseSpamScore(user.id, msg_score) if not ok: return rp.Reply(rp.types.ERR_SPAMMY) return ch.assignMessageId(CachedMessage(user.id))
def cmd_warn(ev, delete=False): c_user = UserContainer(ev.from_user) if ev.reply_to_message is None: return send_answer(ev, rp.Reply(rp.types.ERR_NO_REPLY), True) reply_msid = ch.lookupMapping(ev.from_user.id, data=ev.reply_to_message.message_id) if reply_msid is None: return send_answer(ev, rp.Reply(rp.types.ERR_NOT_IN_CACHE), True) send_answer(ev, core.warn_user(c_user, reply_msid, delete), True)
def cmd_blacklist(ev, arg): c_user = UserContainer(ev.from_user) if ev.reply_to_message is None: return send_answer(ev, rp.Reply(rp.types.ERR_NO_REPLY), True) reply_msid = ch.lookupMapping(ev.from_user.id, data=ev.reply_to_message.message_id) if reply_msid is None: return send_answer(ev, rp.Reply(rp.types.ERR_NOT_IN_CACHE), True) return send_answer(ev, core.blacklist_user(c_user, reply_msid, arg), True)
def cmd_plusone(ev): c_user = UserContainer(ev.from_user) if ev.reply_to_message is None: return send_answer(ev, rp.Reply(rp.types.ERR_NO_REPLY), True) reply_msid = ch.lookupMapping(ev.from_user.id, data=ev.reply_to_message.message_id) if reply_msid is None: return send_answer(ev, rp.Reply(rp.types.ERR_NOT_IN_CACHE), True) return send_answer(ev, core.give_karma(c_user, reply_msid), True)
def remove(user, msid, reason): cm = ch.getMessage(msid) if cm is None or cm.user_id is None: return rp.Reply(rp.types.ERR_NOT_IN_CACHE) user2 = db.getUser(id=cm.user_id) _push_system_message(rp.Reply(rp.types.MESSAGE_REMOVED, reason=reason), who=user2, reply_to=msid) Sender.delete(msid) logging.info("%s had a message removed by %s for: %s", user2, user, reason) return rp.Reply(rp.types.SUCCESS)
def get_info_mod(user, msid): cm = ch.getMessage(msid) if cm is None or cm.user_id is None: return rp.Reply(rp.types.ERR_NOT_IN_CACHE) user2 = db.getUser(id=cm.user_id) params = { "id": user2.getObfuscatedId(), "karma": user2.getObfuscatedKarma(), "cooldown": user2.cooldownUntil if user2.isInCooldown() else None, } return rp.Reply(rp.types.USER_INFO_MOD, **params)
def delete_message(user, msid): if not allow_remove_command: return rp.Reply(rp.types.ERR_COMMAND_DISABLED) cm = ch.getMessage(msid) if cm is None or cm.user_id is None: return rp.Reply(rp.types.ERR_NOT_IN_CACHE) user2 = db.getUser(id=cm.user_id) _push_system_message(rp.Reply(rp.types.MESSAGE_DELETED), who=user2, reply_to=msid) Sender.delete(msid) logging.info("%s deleted a message from [%s]", user, user2.getObfuscatedId()) return rp.Reply(rp.types.SUCCESS)
def set_tripcode(user, text): if not enable_signing: return rp.Reply(rp.types.ERR_COMMAND_DISABLED) if not (0 < text.find("#") < len(text) - 1): return rp.Reply(rp.types.ERR_INVALID_TRIP_FORMAT) if "\n" in text or len(text) > 30: return rp.Reply(rp.types.ERR_INVALID_TRIP_FORMAT) with db.modifyUser(id=user.id) as user: user.tripcode = text tripname, tripcode = genTripcode(user.tripcode) return rp.Reply(rp.types.TRIPCODE_SET, tripname=tripname, tripcode=tripcode)
def blacklist_user(user, msid, reason): cm = ch.getMessage(msid) if cm is None or cm.user_id is None: return rp.Reply(rp.types.ERR_NOT_IN_CACHE) with db.modifyUser(id=cm.user_id) as user2: if user2.rank >= user.rank: return user2.setBlacklisted(reason) Sender.stop_invoked(user2) # do this before queueing new messages below _push_system_message(rp.Reply(rp.types.ERR_BLACKLISTED, reason=reason, contact=blacklist_contact), who=user2, reply_to=msid) Sender.delete(msid) logging.info("%s was blacklisted by %s for: %s", user2, user, reason) return rp.Reply(rp.types.SUCCESS)
def promote_user(user, username2, rank): user2 = getUserByName(username2) if user2 is None: return rp.Reply(rp.types.ERR_NO_USER) if user2.rank >= rank: return with db.modifyUser(id=user2.id) as user2: user2.rank = rank if rank >= RANKS.admin: _push_system_message(rp.Reply(rp.types.PROMOTED_ADMIN), who=user2) elif rank >= RANKS.mod: _push_system_message(rp.Reply(rp.types.PROMOTED_MOD), who=user2) logging.info("%s was promoted by %s to: %d", user2, user, rank) return rp.Reply(rp.types.SUCCESS)
def get_users(user): if user.rank < RANKS.mod: n = sum(1 for user in db.iterateUsers() if user.isJoined()) return rp.Reply(rp.types.USERS_INFO, count=n) active, inactive, black = 0, 0, 0 for user in db.iterateUsers(): if user.isBlacklisted(): black += 1 elif not user.isJoined(): inactive += 1 else: active += 1 return rp.Reply(rp.types.USERS_INFO_EXTENDED, active=active, inactive=inactive, blacklisted=black, total=active + inactive + black)
def toggle_debug(user): with db.modifyUser(id=user.id) as user: user.debugEnabled = not user.debugEnabled new = user.debugEnabled return rp.Reply(rp.types.BOOLEAN_CONFIG, description="Debug mode", enabled=new)
def give_karma(user, msid): cm = ch.getMessage(msid) if cm is None or cm.user_id is None: return rp.Reply(rp.types.ERR_NOT_IN_CACHE) if cm.hasUpvoted(user): return rp.Reply(rp.types.ERR_ALREADY_UPVOTED) elif user.id == cm.user_id: return rp.Reply(rp.types.ERR_UPVOTE_OWN_MESSAGE) cm.addUpvote(user) user2 = db.getUser(id=cm.user_id) with db.modifyUser(id=cm.user_id) as user2: user2.karma += KARMA_PLUS_ONE if not user2.hideKarma: _push_system_message(rp.Reply(rp.types.KARMA_NOTIFICATION), who=user2, reply_to=msid) return rp.Reply(rp.types.KARMA_THANK_YOU)
def toggle_karma(user): with db.modifyUser(id=user.id) as user: user.hideKarma = not user.hideKarma new = user.hideKarma return rp.Reply(rp.types.BOOLEAN_CONFIG, description="Karma notifications", enabled=not new)
def toggle_tripcode(user): with db.modifyUser(id=user.id) as user: user.tripcodeToggle = not user.tripcodeToggle new = user.tripcodeToggle return rp.Reply(rp.types.BOOLEAN_CONFIG, description="Username (tripcode)", enabled=new)
def calc_spam_score(ev): if not allow_message_text(ev.text) or not allow_message_text(ev.caption): return 999 c_user = UserContainer(ev.from_user) user_rank = RANKS[core.get_info(c_user).kwargs["rank"]] if ev.content_type in ("photo", "video", "gif", "sticker", "video/mp4", "document") and user_rank <= 0: if (datetime.now() - core.get_info(c_user).kwargs["joined"]).days <= 1: return 999 else: with db.modifyUser( id=core.get_info(c_user).kwargs["real_id"]) as user: user.rank = RANKS.user_with_images core._push_system_message(rp.Reply(rp.types.IMAGES_ALLOWED), who=user) s = SCORE_BASE_MESSAGE if (ev.forward_from is not None or ev.forward_from_chat is not None or ev.json.get("forward_sender_name") is not None): s = SCORE_BASE_FORWARD if ev.content_type == "sticker": return SCORE_STICKER elif ev.content_type == "text": pass else: return s s += len(ev.text) * SCORE_TEXT_CHARACTER + ev.text.count( "\n") * SCORE_TEXT_LINEBREAK return s
def send_signed_user_message(user, msg_score, text, reply_msid=None, tripcode=False): if not enable_signing: return rp.Reply(rp.types.ERR_COMMAND_DISABLED) if user.isInCooldown(): return rp.Reply(rp.types.ERR_COOLDOWN, until=user.cooldownUntil) ok = spam_scores.increaseSpamScore(user.id, msg_score) if not ok: return rp.Reply(rp.types.ERR_SPAMMY) if not tripcode and sign_interval.total_seconds() > 1: last_used = sign_last_used.get(user.id, None) if last_used and (datetime.now() - last_used) < sign_interval: return rp.Reply(rp.types.ERR_SPAMMY_SIGN) sign_last_used[user.id] = datetime.now() if tripcode: if user.tripcode is None: return rp.Reply(rp.types.ERR_NO_TRIPCODE) tripname, tripcode = genTripcode(user.tripcode) m = rp.Reply(rp.types.TSIGNED_MSG, text=text, user_id=user.id, tripname=tripname, tripcode=tripcode) else: m = rp.Reply(rp.types.SIGNED_MSG, text=text, user_id=user.id, user_text=user.getFormattedName()) msid = ch.assignMessageId(CachedMessage(user.id)) Sender.reply(m, msid, None, user, reply_msid) return msid
def wrapper(c_user, *args, **kwargs): # fetch user from db try: user = db.getUser(id=c_user.id) except KeyError as e: return rp.Reply(rp.types.USER_NOT_IN_CHAT) # check for blacklist or absence if user.isBlacklisted(): return rp.Reply(rp.types.ERR_BLACKLISTED, reason=user.blacklistReason, contact=blacklist_contact) elif not user.isJoined(): return rp.Reply(rp.types.USER_NOT_IN_CHAT) # keep db entry up to date with db.modifyUser(id=user.id) as user: updateUserFromEvent(user, c_user) # call original function return func(user, *args, **kwargs)
def user_join(c_user): try: user = db.getUser(id=c_user.id) except KeyError as e: user = None if user is not None: # check if user can't rejoin err = None if user.isBlacklisted(): err = rp.Reply(rp.types.ERR_BLACKLISTED, reason=user.blacklistReason, contact=blacklist_contact) elif user.isJoined(): err = rp.Reply(rp.types.USER_IN_CHAT) if err is not None: with db.modifyUser(id=user.id) as user: updateUserFromEvent(user, c_user) return err # user rejoins with db.modifyUser(id=user.id) as user: updateUserFromEvent(user, c_user) user.setLeft(False) logging.info("%s rejoined chat", user) return rp.Reply(rp.types.CHAT_JOIN) # create new user user = User() user.defaults() user.id = c_user.id updateUserFromEvent(user, c_user) if not any(db.iterateUserIds()): user.rank = RANKS.admin logging.info("%s joined chat", user) db.addUser(user) ret = [rp.Reply(rp.types.CHAT_JOIN)] motd = db.getSystemConfig().motd if motd != "": ret.append(rp.Reply(rp.types.CUSTOM, text=motd)) return ret
def warn_user(user, msid, delete=False): cm = ch.getMessage(msid) if cm is None or cm.user_id is None: return rp.Reply(rp.types.ERR_NOT_IN_CACHE) if not cm.warned: with db.modifyUser(id=cm.user_id) as user2: d = user2.addWarning() user2.karma -= KARMA_WARN_PENALTY _push_system_message(rp.Reply(rp.types.GIVEN_COOLDOWN, duration=d, deleted=delete), who=user2, reply_to=msid) cm.warned = True else: user2 = db.getUser(id=cm.user_id) if not delete: # allow deleting already warned messages return rp.Reply(rp.types.ERR_ALREADY_WARNED) if delete: Sender.delete(msid) logging.info("%s warned [%s]%s", user, user2.getObfuscatedId(), delete and " (message deleted)" or "") return rp.Reply(rp.types.SUCCESS)
def uncooldown_user(user, oid2=None, username2=None): if oid2 is not None: user2 = getUserByOid(oid2) if user2 is None: return rp.Reply(rp.types.ERR_NO_USER_BY_ID) elif username2 is not None: user2 = getUserByName(username2) if user2 is None: return rp.Reply(rp.types.ERR_NO_USER) else: raise ValueError() if not user2.isInCooldown(): return rp.Reply(rp.types.ERR_NOT_IN_COOLDOWN) with db.modifyUser(id=user2.id) as user2: user2.removeWarning() was_until = user2.cooldownUntil user2.cooldownUntil = None logging.info("%s removed cooldown from %s (was until %s)", user, user2, format_datetime(was_until)) return rp.Reply(rp.types.SUCCESS)
def get_info(user): params = { "id": user.getObfuscatedId(), "username": user.getFormattedName(), "rank_i": user.rank, "rank": RANKS.reverse[user.rank], "karma": user.karma, "warnings": user.warnings, "warnExpiry": user.warnExpiry, "cooldown": user.cooldownUntil if user.isInCooldown() else None, } return rp.Reply(rp.types.USER_INFO, **params)
def send_signed_user_message(user, msg_score, text, reply_msid=None, tripcode=False): if not enable_signing: return rp.Reply(rp.types.ERR_COMMAND_DISABLED) if user.isInCooldown(): return rp.Reply(rp.types.ERR_COOLDOWN, until=user.cooldownUntil) ok = spam_scores.increaseSpamScore(user.id, msg_score) if not ok: return rp.Reply(rp.types.ERR_SPAMMY) if tripcode: if user.tripcode is None: return rp.Reply(rp.types.ERR_NO_TRIPCODE) tripname, tripcode = genTripcode(user.tripcode) m = rp.Reply(rp.types.TSIGNED_MSG, text=text, user_id=user.id, tripname=tripname, tripcode=tripcode) else: m = rp.Reply(rp.types.SIGNED_MSG, text=text, user_id=user.id, user_text=user.getFormattedName()) msid = ch.assignMessageId(CachedMessage(user.id)) Sender.reply(m, msid, None, user, reply_msid) return msid
def prepare_user_message(user: User, msg_score, *, is_media=False, signed=False, tripcode=False): # prerequisites if user.isInCooldown(): return rp.Reply(rp.types.ERR_COOLDOWN, until=user.cooldownUntil) if (signed or tripcode) and not enable_signing: return rp.Reply(rp.types.ERR_COMMAND_DISABLED) if tripcode and user.tripcode is None: return rp.Reply(rp.types.ERR_NO_TRIPCODE) if is_media and user.rank < RANKS.mod and media_limit_period is not None: if (datetime.now() - user.joined) < media_limit_period: return rp.Reply(rp.types.ERR_MEDIA_LIMIT) ok = spam_scores.increaseSpamScore(user.id, msg_score) if not ok: return rp.Reply(rp.types.ERR_SPAMMY) # enforce signing cooldown if signed and sign_interval.total_seconds() > 1: last_used = sign_last_used.get(user.id, None) if last_used and (datetime.now() - last_used) < sign_interval: return rp.Reply(rp.types.ERR_SPAMMY_SIGN) sign_last_used[user.id] = datetime.now() return ch.assignMessageId(CachedMessage(user.id))
def get_info(user): params = { "id": user.getObfuscatedId(), "username": user.getFormattedName(), "rank_i": user.rank, "rank": RANKS.reverse[user.rank], "karma": user.karma, "warnings": user.warnings, "warnExpiry": user.warnExpiry, "cooldown": user.cooldownUntil if user.isInCooldown() else None, } if enable_signing: params["tripcode"] = user.tripcode else: params['tripcode'] = "disabled" return rp.Reply(rp.types.USER_INFO, **params)
def cmd_version(ev): send_answer(ev, rp.Reply(rp.types.PROGRAM_VERSION, version=VERSION), True)
def cmd_adminhelp(ev): send_answer(ev, rp.Reply(rp.types.HELP_ADMIN), True)
def cmd_modhelp(ev): send_answer(ev, rp.Reply(rp.types.HELP_MODERATOR), True)
def send_admin_message(user, arg): text = arg + " ~<b>admins</b>" m = rp.Reply(rp.types.CUSTOM, text=text) _push_system_message(m) logging.info("%s sent admin message: %s", user, arg)
def get_tripcode(user): if not enable_signing: return rp.Reply(rp.types.ERR_COMMAND_DISABLED) return rp.Reply(rp.types.TRIPCODE_INFO, tripcode=user.tripcode)