mirror of
https://gitlab.com/vylion/velascobot.git
synced 2025-04-19 21:46:35 +02:00
Reverted back to non-clean startup
Added a memory of last C updated chats (default: 20)
This commit is contained in:
parent
bb00efe1d1
commit
895e1cd843
4 changed files with 138 additions and 56 deletions
|
@ -27,10 +27,10 @@ class Archivist(object):
|
||||||
self.bypass = bypass
|
self.bypass = bypass
|
||||||
|
|
||||||
def chat_folder(self, *formatting, **key_format):
|
def chat_folder(self, *formatting, **key_format):
|
||||||
return ("./" + self.chatdir + "chat_{tag}").format(*formatting, **key_format)
|
return (self.chatdir + "chat_{tag}").format(*formatting, **key_format)
|
||||||
|
|
||||||
def chat_file(self, *formatting, **key_format):
|
def chat_file(self, *formatting, **key_format):
|
||||||
return ("./" + self.chatdir + "chat_{tag}/{file}{ext}").format(*formatting, **key_format)
|
return (self.chatdir + "chat_{tag}/{file}{ext}").format(*formatting, **key_format)
|
||||||
|
|
||||||
def store(self, tag, data, gen):
|
def store(self, tag, data, gen):
|
||||||
chat_folder = self.chat_folder(tag=tag)
|
chat_folder = self.chat_folder(tag=tag)
|
||||||
|
|
66
memorylist.py
Normal file
66
memorylist.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from collections.abc import MutableSequence
|
||||||
|
|
||||||
|
|
||||||
|
class MemoryList(MutableSequence):
|
||||||
|
def __init__(self, capacity, data=None):
|
||||||
|
"""Initialize the class"""
|
||||||
|
super(MemoryList, self).__init__()
|
||||||
|
self._capacity = capacity
|
||||||
|
if (data is not None):
|
||||||
|
self._list = list(data)
|
||||||
|
else:
|
||||||
|
self._list = list()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<{0} {1}, capacity {2}>".format(self.__class__.__name__, self._list, self._capacity)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
"""List length"""
|
||||||
|
return len(self._list)
|
||||||
|
|
||||||
|
def capacity(self):
|
||||||
|
return self._capacity
|
||||||
|
|
||||||
|
def __getitem__(self, ii):
|
||||||
|
"""Get a list item"""
|
||||||
|
return self._list[ii]
|
||||||
|
|
||||||
|
def __delitem__(self, ii):
|
||||||
|
"""Delete an item"""
|
||||||
|
del self._list[ii]
|
||||||
|
|
||||||
|
def __setitem__(self, ii, val):
|
||||||
|
self._list[ii] = val
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self._list)
|
||||||
|
|
||||||
|
def __contains__(self, val):
|
||||||
|
return val in self._list
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self._list.__iter__()
|
||||||
|
|
||||||
|
def insert(self, ii, val):
|
||||||
|
self._list.insert(ii, val)
|
||||||
|
|
||||||
|
def append(self, val):
|
||||||
|
if val in self._list:
|
||||||
|
self._list.remove(val)
|
||||||
|
|
||||||
|
self._list.append(val)
|
||||||
|
if len(self._list) >= self._capacity:
|
||||||
|
x = self._list[0]
|
||||||
|
del self._list[0]
|
||||||
|
return x
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_next(self, cond):
|
||||||
|
val = next((v for v in self._list if cond(v)), None)
|
||||||
|
if val is not None:
|
||||||
|
self._list.remove(val)
|
||||||
|
self._list.append(val)
|
||||||
|
return val
|
120
speaker.py
120
speaker.py
|
@ -2,10 +2,15 @@
|
||||||
|
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
|
from memorylist import MemoryList
|
||||||
from reader import Reader, get_chat_title
|
from reader import Reader, get_chat_title
|
||||||
from telegram.error import TimedOut, NetworkError
|
from telegram.error import TimedOut, NetworkError
|
||||||
|
|
||||||
|
|
||||||
|
def eprint(*args, **kwargs):
|
||||||
|
print(*args, file=sys.stderr, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def send(bot, cid, text, replying=None, formatting=None, logger=None, **kwargs):
|
def send(bot, cid, text, replying=None, formatting=None, logger=None, **kwargs):
|
||||||
kwargs["parse_mode"] = formatting
|
kwargs["parse_mode"] = formatting
|
||||||
kwargs["reply_to_message_id"] = replying
|
kwargs["reply_to_message_id"] = replying
|
||||||
|
@ -13,7 +18,8 @@ def send(bot, cid, text, replying=None, formatting=None, logger=None, **kwargs):
|
||||||
if text.startswith(Reader.TAG_PREFIX):
|
if text.startswith(Reader.TAG_PREFIX):
|
||||||
words = text.split(maxsplit=1)
|
words = text.split(maxsplit=1)
|
||||||
if logger:
|
if logger:
|
||||||
logger.info('Sending {} "{}" to {}'.format(words[0][4:-1], words[1], cid))
|
# logger.info('Sending {} "{}" to {}'.format(words[0][4:-1], words[1], cid))
|
||||||
|
eprint('.')
|
||||||
# Logs something like 'Sending VIDEO "VIDEO_ID" to CHAT_ID'
|
# Logs something like 'Sending VIDEO "VIDEO_ID" to CHAT_ID'
|
||||||
|
|
||||||
if words[0] == Reader.STICKER_TAG:
|
if words[0] == Reader.STICKER_TAG:
|
||||||
|
@ -35,7 +41,8 @@ class Speaker(object):
|
||||||
ModeChance = "MODE_CHANCE"
|
ModeChance = "MODE_CHANCE"
|
||||||
|
|
||||||
def __init__(self, username, archivist, logger, nicknames=[], mute_time=60,
|
def __init__(self, username, archivist, logger, nicknames=[], mute_time=60,
|
||||||
reply=0.1, repeat=0.05, wakeup=False, mode=ModeFixed
|
reply=0.1, repeat=0.05, wakeup=False, mode=ModeFixed,
|
||||||
|
memory=20
|
||||||
):
|
):
|
||||||
self.names = nicknames
|
self.names = nicknames
|
||||||
self.mute_time = mute_time
|
self.mute_time = mute_time
|
||||||
|
@ -51,8 +58,8 @@ class Speaker(object):
|
||||||
self.repeat = repeat
|
self.repeat = repeat
|
||||||
self.filter_cids = archivist.filter_cids
|
self.filter_cids = archivist.filter_cids
|
||||||
self.bypass = archivist.bypass
|
self.bypass = archivist.bypass
|
||||||
self.current_reader = None
|
|
||||||
self.time_counter = None
|
self.time_counter = None
|
||||||
|
self.memory = MemoryList(memory)
|
||||||
|
|
||||||
def announce(self, bot, announcement, check=(lambda _: True)):
|
def announce(self, bot, announcement, check=(lambda _: True)):
|
||||||
# Sends an announcement to all chats that pass the check
|
# Sends an announcement to all chats that pass the check
|
||||||
|
@ -74,25 +81,31 @@ class Speaker(object):
|
||||||
return reader.check_type("group")
|
return reader.check_type("group")
|
||||||
self.announce(bot, wake, group_check)
|
self.announce(bot, wake, group_check)
|
||||||
|
|
||||||
|
def get_reader(self, cid):
|
||||||
|
return self.memory.get_next(lambda r: r.cid() == cid)
|
||||||
|
|
||||||
def load_reader(self, chat):
|
def load_reader(self, chat):
|
||||||
cid = str(chat.id)
|
cid = str(chat.id)
|
||||||
if self.current_reader is not None and cid == self.current_reader.cid():
|
reader = self.get_reader(cid)
|
||||||
return
|
if reader is not None:
|
||||||
|
return reader
|
||||||
if self.current_reader is not None:
|
|
||||||
self.current_reader.commit_memory()
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
reader = self.archivist.get_reader(cid)
|
reader = self.archivist.get_reader(cid)
|
||||||
if not reader:
|
if not reader:
|
||||||
reader = Reader.FromChat(chat, self.archivist.max_period, self.logger)
|
reader = Reader.FromChat(chat, self.archivist.max_period, self.logger)
|
||||||
self.current_reader = reader
|
|
||||||
|
|
||||||
def get_reader(self, cid):
|
old_reader = self.memory.append(reader)
|
||||||
if self.current_reader is None or cid != self.current_reader.cid():
|
if old_reader is not None:
|
||||||
|
old_reader.commit_memory()
|
||||||
|
self.store(old_reader)
|
||||||
|
|
||||||
|
return reader
|
||||||
|
|
||||||
|
def access_reader(self, cid):
|
||||||
|
reader = self.get_reader(cid)
|
||||||
|
if reader is None:
|
||||||
return self.archivist.get_reader(cid)
|
return self.archivist.get_reader(cid)
|
||||||
|
return reader
|
||||||
return self.current_reader
|
|
||||||
|
|
||||||
def mentioned(self, text):
|
def mentioned(self, text):
|
||||||
if self.username in text:
|
if self.username in text:
|
||||||
|
@ -102,11 +115,14 @@ class Speaker(object):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def should_reply(self, message):
|
def is_mute(self):
|
||||||
current_time = int(time.perf_counter())
|
current_time = int(time.perf_counter())
|
||||||
if self.time_counter is not None and (current_time - self.time_counter) < self.mute_time:
|
return self.time_counter is not None and (current_time - self.time_counter) < self.mute_time
|
||||||
|
|
||||||
|
def should_reply(self, message, reader):
|
||||||
|
if self.is_mute():
|
||||||
return False
|
return False
|
||||||
if not self.bypass and self.current_reader.is_restricted():
|
if not self.bypass and reader.is_restricted():
|
||||||
user = message.chat.get_member(message.from_user.id)
|
user = message.chat.get_member(message.from_user.id)
|
||||||
if not self.user_is_admin(user):
|
if not self.user_is_admin(user):
|
||||||
# update.message.reply_text("You do not have permissions to do that.")
|
# update.message.reply_text("You do not have permissions to do that.")
|
||||||
|
@ -116,40 +132,38 @@ class Speaker(object):
|
||||||
return (((replied is not None) and (replied.from_user.name == self.username))
|
return (((replied is not None) and (replied.from_user.name == self.username))
|
||||||
or (self.mentioned(text)))
|
or (self.mentioned(text)))
|
||||||
|
|
||||||
def save(self):
|
def store(self, reader):
|
||||||
if self.current_reader is None:
|
if reader is None:
|
||||||
raise ValueError("Tried to store a None Reader.")
|
raise ValueError("Tried to store a None Reader.")
|
||||||
else:
|
else:
|
||||||
self.archivist.store(*self.current_reader.archive())
|
self.archivist.store(*reader.archive())
|
||||||
|
|
||||||
def read(self, update, context):
|
def read(self, update, context):
|
||||||
if update.message is None:
|
if update.message is None:
|
||||||
return
|
return
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
self.load_reader(chat)
|
reader = self.load_reader(chat)
|
||||||
self.current_reader.read(update.message)
|
reader.read(update.message)
|
||||||
|
|
||||||
if self.should_reply(update.message) and self.current_reader.is_answering():
|
if self.should_reply(update.message, reader) and reader.is_answering():
|
||||||
self.say(context.bot, replying=update.message.message_id)
|
self.say(context.bot, reader, replying=update.message.message_id)
|
||||||
return
|
return
|
||||||
|
|
||||||
title = get_chat_title(update.message.chat)
|
title = get_chat_title(update.message.chat)
|
||||||
if title != self.current_reader.title():
|
if title != reader.title():
|
||||||
self.current_reader.set_title(title)
|
reader.set_title(title)
|
||||||
|
|
||||||
self.current_reader.countdown -= 1
|
reader.countdown -= 1
|
||||||
if self.current_reader.countdown < 0:
|
if reader.countdown < 0:
|
||||||
self.current_reader.reset_countdown()
|
reader.reset_countdown()
|
||||||
rid = self.current_reader.random_memory() if random.random() <= self.reply else None
|
rid = reader.random_memory() if random.random() <= self.reply else None
|
||||||
self.say(context.bot, replying=rid)
|
self.say(context.bot, reader, replying=rid)
|
||||||
elif (self.current_reader.period() - self.current_reader.countdown) % self.archivist.save_count == 0:
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
def speak(self, update, context):
|
def speak(self, update, context):
|
||||||
chat = (update.message.chat)
|
chat = (update.message.chat)
|
||||||
self.load_reader(chat)
|
reader = self.load_reader(chat)
|
||||||
|
|
||||||
if not self.bypass and self.current_reader.is_restricted():
|
if not self.bypass and reader.is_restricted():
|
||||||
user = update.message.chat.get_member(update.message.from_user.id)
|
user = update.message.chat.get_member(update.message.from_user.id)
|
||||||
if not self.user_is_admin(user):
|
if not self.user_is_admin(user):
|
||||||
# update.message.reply_text("You do not have permissions to do that.")
|
# update.message.reply_text("You do not have permissions to do that.")
|
||||||
|
@ -160,31 +174,33 @@ class Speaker(object):
|
||||||
rid = replied.message_id if replied else mid
|
rid = replied.message_id if replied else mid
|
||||||
words = update.message.text.split()
|
words = update.message.text.split()
|
||||||
if len(words) > 1:
|
if len(words) > 1:
|
||||||
self.current_reader.read(' '.join(words[1:]))
|
reader.read(' '.join(words[1:]))
|
||||||
self.say(context.bot, replying=rid)
|
self.say(context.bot, reader, replying=rid)
|
||||||
|
|
||||||
def user_is_admin(self, member):
|
def user_is_admin(self, member):
|
||||||
self.logger.info("user {} ({}) requesting a restricted action".format(str(member.user.id), member.user.name))
|
# self.logger.info("user {} ({}) requesting a restricted action".format(str(member.user.id), member.user.name))
|
||||||
# self.logger.info("Bot Creator ID is {}".format(str(self.archivist.admin)))
|
# self.logger.info("Bot Creator ID is {}".format(str(self.archivist.admin)))
|
||||||
return ((member.status == 'creator')
|
return ((member.status == 'creator')
|
||||||
or (member.status == 'administrator')
|
or (member.status == 'administrator')
|
||||||
or (member.user.id == self.archivist.admin))
|
or (member.user.id == self.archivist.admin))
|
||||||
|
|
||||||
def speech(self):
|
def speech(self, reader):
|
||||||
return self.current_reader.generate_message(self.archivist.max_len)
|
return reader.generate_message(self.archivist.max_len)
|
||||||
|
|
||||||
def say(self, bot, replying=None, **kwargs):
|
def say(self, bot, reader, replying=None, **kwargs):
|
||||||
cid = self.current_reader.cid()
|
cid = reader.cid()
|
||||||
if self.filter_cids is not None and cid not in self.filter_cids:
|
if self.filter_cids is not None and cid not in self.filter_cids:
|
||||||
return
|
return
|
||||||
|
if self.is_mute():
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
send(bot, cid, self.speech(), replying, logger=self.logger, **kwargs)
|
send(bot, cid, self.speech(reader), replying, logger=self.logger, **kwargs)
|
||||||
if self.bypass:
|
if self.bypass:
|
||||||
max_period = self.archivist.max_period
|
max_period = self.archivist.max_period
|
||||||
self.current_reader.set_period(random.randint(max_period // 4, max_period))
|
reader.set_period(random.randint(max_period // 4, max_period))
|
||||||
if random.random() <= self.repeat:
|
if random.random() <= self.repeat:
|
||||||
send(bot, cid, self.speech(), logger=self.logger, **kwargs)
|
send(bot, cid, self.speech(reader), logger=self.logger, **kwargs)
|
||||||
except TimedOut as e:
|
except TimedOut as e:
|
||||||
self.logger.error("Telegram timed out.")
|
self.logger.error("Telegram timed out.")
|
||||||
self.logger.exception(e)
|
self.logger.exception(e)
|
||||||
|
@ -201,7 +217,7 @@ class Speaker(object):
|
||||||
|
|
||||||
def get_count(self, update, context):
|
def get_count(self, update, context):
|
||||||
cid = str(update.message.chat.id)
|
cid = str(update.message.chat.id)
|
||||||
reader = self.get_reader(cid)
|
reader = self.access_reader(cid)
|
||||||
|
|
||||||
num = str(reader.count()) if reader else "no"
|
num = str(reader.count()) if reader else "no"
|
||||||
update.message.reply_text("I remember {} messages.".format(num))
|
update.message.reply_text("I remember {} messages.".format(num))
|
||||||
|
@ -213,7 +229,7 @@ class Speaker(object):
|
||||||
|
|
||||||
def period(self, update, context):
|
def period(self, update, context):
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
reader = self.get_reader(str(chat.id))
|
reader = self.access_reader(str(chat.id))
|
||||||
|
|
||||||
words = update.message.text.split()
|
words = update.message.text.split()
|
||||||
if len(words) <= 1:
|
if len(words) <= 1:
|
||||||
|
@ -235,7 +251,7 @@ class Speaker(object):
|
||||||
|
|
||||||
def answer(self, update, context):
|
def answer(self, update, context):
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
reader = self.get_reader(str(chat.id))
|
reader = self.access_reader(str(chat.id))
|
||||||
|
|
||||||
words = update.message.text.split()
|
words = update.message.text.split()
|
||||||
if len(words) <= 1:
|
if len(words) <= 1:
|
||||||
|
@ -261,7 +277,7 @@ class Speaker(object):
|
||||||
return
|
return
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
user = chat.get_member(update.message.from_user.id)
|
user = chat.get_member(update.message.from_user.id)
|
||||||
reader = self.get_reader(str(chat.id))
|
reader = self.access_reader(str(chat.id))
|
||||||
|
|
||||||
if reader.is_restricted():
|
if reader.is_restricted():
|
||||||
if not self.user_is_admin(user):
|
if not self.user_is_admin(user):
|
||||||
|
@ -278,7 +294,7 @@ class Speaker(object):
|
||||||
return
|
return
|
||||||
chat = update.message.chat
|
chat = update.message.chat
|
||||||
user = chat.get_member(update.message.from_user.id)
|
user = chat.get_member(update.message.from_user.id)
|
||||||
reader = self.get_reader(str(chat.id))
|
reader = self.access_reader(str(chat.id))
|
||||||
|
|
||||||
if reader.is_restricted():
|
if reader.is_restricted():
|
||||||
if not self.user_is_admin(user):
|
if not self.user_is_admin(user):
|
||||||
|
@ -294,7 +310,7 @@ class Speaker(object):
|
||||||
usr = msg.from_user
|
usr = msg.from_user
|
||||||
cht = msg.chat
|
cht = msg.chat
|
||||||
chtname = cht.title if cht.title else cht.first_name
|
chtname = cht.title if cht.title else cht.first_name
|
||||||
rdr = self.get_reader(str(cht.id))
|
rdr = self.access_reader(str(cht.id))
|
||||||
|
|
||||||
answer = ("You're **{name}**, with username `{username}`, and "
|
answer = ("You're **{name}**, with username `{username}`, and "
|
||||||
"id `{uid}`.\nYou're messaging in the chat named __{cname}__,"
|
"id `{uid}`.\nYou're messaging in the chat named __{cname}__,"
|
||||||
|
@ -308,7 +324,7 @@ class Speaker(object):
|
||||||
def where(self, update, context):
|
def where(self, update, context):
|
||||||
msg = update.message
|
msg = update.message
|
||||||
chat = msg.chat
|
chat = msg.chat
|
||||||
reader = self.get_reader(str(chat.id))
|
reader = self.access_reader(str(chat.id))
|
||||||
if reader.is_restricted() and reader.is_silenced():
|
if reader.is_restricted() and reader.is_silenced():
|
||||||
permissions = "restricted and silenced"
|
permissions = "restricted and silenced"
|
||||||
elif reader.is_restricted():
|
elif reader.is_restricted():
|
||||||
|
|
|
@ -89,7 +89,7 @@ def main():
|
||||||
filter_cids.append(str(args.admin_id))
|
filter_cids.append(str(args.admin_id))
|
||||||
|
|
||||||
archivist = Archivist(logger,
|
archivist = Archivist(logger,
|
||||||
chatdir="chatlogs/",
|
chatdir="./chatlogs/",
|
||||||
chatext=".vls",
|
chatext=".vls",
|
||||||
admin=args.admin_id,
|
admin=args.admin_id,
|
||||||
filter_cids=filter_cids,
|
filter_cids=filter_cids,
|
||||||
|
@ -130,7 +130,7 @@ def main():
|
||||||
speakerbot.wake(updater.bot, wake_msg)
|
speakerbot.wake(updater.bot, wake_msg)
|
||||||
|
|
||||||
# Start the Bot
|
# Start the Bot
|
||||||
updater.start_polling(clean=True)
|
updater.start_polling()
|
||||||
|
|
||||||
# Run the bot until you press Ctrl-C or the process receives SIGINT,
|
# Run the bot until you press Ctrl-C or the process receives SIGINT,
|
||||||
# SIGTERM or SIGABRT. This should be used most of the time, since
|
# SIGTERM or SIGABRT. This should be used most of the time, since
|
||||||
|
|
Loading…
Reference in a new issue