Update files
This commit is contained in:
parent
81030d1297
commit
73d8dd543f
5 changed files with 191 additions and 9 deletions
|
|
@ -2,12 +2,14 @@ import telebot
|
|||
import logging
|
||||
import time
|
||||
import json
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
|
||||
LONG_LONG_TIME = datetime(2100, 1, 1)
|
||||
ID_REMIND_DURATION = timedelta(days=2)
|
||||
BAN_NOTSENT_WARNING = timedelta(minutes=10)
|
||||
REMINDER_CHECK_INTERVAL = 300 # seconds between checks
|
||||
ALL_CONTENT_TYPES = ('animation', 'audio', 'contact', 'dice', 'document',
|
||||
'game', 'location', 'photo', 'sticker', 'story', 'text', 'venue', 'video',
|
||||
'video_note', 'voice')
|
||||
|
|
@ -16,6 +18,7 @@ TMessage = telebot.types.Message
|
|||
|
||||
bot: telebot.TeleBot = None
|
||||
db = None
|
||||
db_lock = threading.Lock()
|
||||
|
||||
bot_self_id: int = None
|
||||
target_group: Optional[int] = None
|
||||
|
|
@ -23,9 +26,10 @@ message_thread_id: Optional[int] = None
|
|||
welcome_text: str = None
|
||||
reply_text: str = None
|
||||
integration_fmt: Optional[str] = None
|
||||
reply_reminder_interval: timedelta = timedelta(hours=6)
|
||||
|
||||
def init(config: dict, _db):
|
||||
global bot, db, bot_self_id, target_group, message_thread_id, welcome_text, reply_text, integration_fmt
|
||||
global bot, db, bot_self_id, target_group, message_thread_id, welcome_text, reply_text, integration_fmt, reply_reminder_interval
|
||||
if not config.get("bot_token"):
|
||||
logging.error("No telegram token specified.")
|
||||
exit(1)
|
||||
|
|
@ -41,6 +45,8 @@ def init(config: dict, _db):
|
|||
welcome_text = config["welcome_text"]
|
||||
reply_text = config["reply_text"]
|
||||
integration_fmt = config.get("integration_fmt")
|
||||
if config.get("reminder_interval_minutes"):
|
||||
reply_reminder_interval = timedelta(minutes=int(config["reminder_interval_minutes"]))
|
||||
|
||||
set_handler(handle_msg, content_types=ALL_CONTENT_TYPES)
|
||||
bot_self_id = bot.get_me().id
|
||||
|
|
@ -101,7 +107,8 @@ class ModificationContext():
|
|||
return self.obj
|
||||
def __exit__(self, exc_type, *_):
|
||||
if exc_type is None:
|
||||
db[self.key] = self.obj
|
||||
with db_lock:
|
||||
db[self.key] = self.obj
|
||||
|
||||
class User():
|
||||
id: int
|
||||
|
|
@ -109,12 +116,18 @@ class User():
|
|||
realname: str
|
||||
last_messaged: datetime
|
||||
banned_until: Optional[datetime]
|
||||
replied_to: bool
|
||||
last_reminder_sent: Optional[datetime]
|
||||
last_fwd_msg_id: Optional[int]
|
||||
def __init__(self):
|
||||
self.id = None
|
||||
self.username = None
|
||||
self.realname = None
|
||||
self.last_messaged = None
|
||||
self.banned_until = None
|
||||
self.replied_to = True
|
||||
self.last_reminder_sent = None
|
||||
self.last_fwd_msg_id = None
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, User):
|
||||
return self.id == other.id
|
||||
|
|
@ -123,6 +136,8 @@ class User():
|
|||
return "<User id=%d>" % self.id
|
||||
def defaults(self):
|
||||
self.last_messaged = datetime(1970, 1, 1)
|
||||
self.replied_to = True
|
||||
self.last_reminder_sent = None
|
||||
|
||||
# this is kinda shit
|
||||
db_last_sync = 0
|
||||
|
|
@ -131,15 +146,18 @@ def db_auto_sync():
|
|||
now = int(time.time())
|
||||
if now > db_last_sync + 15:
|
||||
db_last_sync = now
|
||||
db.sync()
|
||||
with db_lock:
|
||||
db.sync()
|
||||
#
|
||||
|
||||
def db_get_user(id) -> User:
|
||||
return db["u%d" % id]
|
||||
with db_lock:
|
||||
return db["u%d" % id]
|
||||
|
||||
def db_modify_user(id, allow_new=False) -> ModificationContext:
|
||||
key = "u%d" % id
|
||||
obj = db.get(key)
|
||||
with db_lock:
|
||||
obj = db.get(key)
|
||||
if obj is None:
|
||||
if allow_new:
|
||||
obj = User()
|
||||
|
|
@ -165,7 +183,8 @@ def handle_group(ev: TMessage):
|
|||
if ev.reply_to_message.from_user.id != bot_self_id:
|
||||
return
|
||||
|
||||
user_id = db.get("m%d" % ev.reply_to_message.message_id)
|
||||
with db_lock:
|
||||
user_id = db.get("m%d" % ev.reply_to_message.message_id)
|
||||
logging.debug("found id = %d mapped to user %s", ev.reply_to_message.message_id, user_id)
|
||||
if user_id is None:
|
||||
logging.warning("Couldn't find replied to message in target group")
|
||||
|
|
@ -212,6 +231,12 @@ def handle_group_command(ev: TMessage, user_id: int, c: str, arg: str):
|
|||
user.banned_until = None
|
||||
msg = "User was unbanned."
|
||||
return callwrapper(lambda: bot.send_message(target_group, message_thread_id=message_thread_id, text=msg))
|
||||
elif c == "finished":
|
||||
with db_modify_user(user_id) as user:
|
||||
user.replied_to = True
|
||||
user.last_reminder_sent = None
|
||||
msg = "Marked as replied. Reminders stopped."
|
||||
return callwrapper(lambda: bot.send_message(target_group, message_thread_id=message_thread_id, text=msg))
|
||||
|
||||
def handle_private(ev: TMessage):
|
||||
if target_group is None:
|
||||
|
|
@ -259,15 +284,22 @@ def handle_private(ev: TMessage):
|
|||
callwrapper(lambda: bot.send_message(chat_id=target_group, message_thread_id=message_thread_id, text=msg, parse_mode="HTML"))
|
||||
def f(user_id=user.id):
|
||||
ev2 = bot.forward_message(chat_id=target_group, from_chat_id=ev.chat.id, message_id=ev.message_id, message_thread_id=message_thread_id)
|
||||
db["m%d" % ev2.message_id] = user_id
|
||||
with db_lock:
|
||||
db["m%d" % ev2.message_id] = user_id
|
||||
with db_modify_user(user_id) as u:
|
||||
u.last_fwd_msg_id = ev2.message_id
|
||||
logging.debug("delivered msg from %s -> id = %d", user, ev2.message_id)
|
||||
callwrapper(f)
|
||||
res = callwrapper(f)
|
||||
if res == "blocked":
|
||||
return
|
||||
|
||||
if reply_text:
|
||||
callwrapper(lambda: bot.send_message(ev.chat.id, reply_text, parse_mode="HTML"))
|
||||
|
||||
with db_modify_user(user.id) as user:
|
||||
user.last_messaged = now
|
||||
user.replied_to = False
|
||||
user.last_reminder_sent = now # first reminder fires REPLY_REMINDER_INTERVAL from now
|
||||
|
||||
def handle_private_command(ev: TMessage, user: User, c):
|
||||
if c == "start":
|
||||
|
|
@ -276,6 +308,55 @@ def handle_private_command(ev: TMessage, user: User, c):
|
|||
elif c == "stop":
|
||||
return True
|
||||
|
||||
### Reminders
|
||||
|
||||
def reminder_loop():
|
||||
while True:
|
||||
try:
|
||||
_send_pending_reminders()
|
||||
except Exception:
|
||||
logging.exception("Exception in reminder loop")
|
||||
time.sleep(REMINDER_CHECK_INTERVAL)
|
||||
|
||||
def _send_pending_reminders():
|
||||
if target_group is None:
|
||||
return
|
||||
now = datetime.now()
|
||||
with db_lock:
|
||||
keys = [k for k in db.keys() if k.startswith("u")]
|
||||
to_remind = []
|
||||
for key in keys:
|
||||
user = db.get(key)
|
||||
if not isinstance(user, User):
|
||||
continue
|
||||
if getattr(user, 'replied_to', True):
|
||||
continue
|
||||
last_reminder = getattr(user, 'last_reminder_sent', None)
|
||||
if last_reminder is not None and now - last_reminder < reply_reminder_interval:
|
||||
continue
|
||||
to_remind.append(user)
|
||||
for user in to_remind:
|
||||
try:
|
||||
_send_reminder(user, now)
|
||||
except Exception:
|
||||
logging.exception("Failed to send reminder for user %d", user.id)
|
||||
|
||||
def _send_reminder(user: User, now: datetime):
|
||||
fwd_msg_id = getattr(user, 'last_fwd_msg_id', None)
|
||||
if fwd_msg_id is None:
|
||||
return
|
||||
def f():
|
||||
bot.send_message(chat_id=target_group, message_thread_id=message_thread_id,
|
||||
text="\U0001f514 Pending reply", reply_to_message_id=fwd_msg_id)
|
||||
status = callwrapper(f)
|
||||
if status:
|
||||
logging.warning("Reminder failed for user %d: %s", user.id, status)
|
||||
return
|
||||
with db_modify_user(user.id) as u:
|
||||
u.replied_to = False
|
||||
u.last_reminder_sent = now
|
||||
logging.info("Sent reminder for user %d", user.id)
|
||||
|
||||
### Helpers
|
||||
|
||||
def str_is_printable(s):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue