diff --git a/README.md b/README.md
index 99572da..e2de4ce 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,10 @@
# RandomTea
![](logo.png)
+
Random meetings for chats, kind of like RandomCoffee.
-### How it works
+## How it works
1. The bot is invited to a chat
2. The members of the chat open the bot
diff --git a/bot.py b/bot.py
index 3c00d6e..66e7423 100644
--- a/bot.py
+++ b/bot.py
@@ -1,6 +1,8 @@
import telebot
import pymongo
from functools import wraps
+
+from community import Community
from user import User
from templates import TemplateProvider
import emoji_data_python as edp
@@ -11,11 +13,12 @@ import traceback
import time
-def locale_from_ietf(ietf_locale: str) -> str:
- if not ietf_locale:
- return 'en'
- ietf_locale = ietf_locale.split('-')[0]
- return 'ru' if ietf_locale in ['ru', 'be', 'uk'] else 'en'
+def locale_from_ietf(_ietf_locale: str) -> str:
+ # if not ietf_locale:
+ # return 'en'
+ # ietf_locale = ietf_locale.split('-')[0]
+ # return 'ru' if ietf_locale in ['ru', 'be', 'uk'] else 'en'
+ return 'ru'
class Bot(telebot.TeleBot):
@@ -77,6 +80,15 @@ class Bot(telebot.TeleBot):
return None
return User.from_dict(data)
+ def get_community_from_db(self, request: dict, create_if_not_found: bool = False) -> typing.Optional[Community]:
+ data = self.db.communities.find_one(request)
+ if data is None:
+ if create_if_not_found:
+ self.db.communities.insert_one(request)
+ return Community.from_dict(request)
+ return None
+ return Community.from_dict(data)
+
def get_user_by_msg(self, msg: typing.Union[telebot.types.Message, telebot.types.InlineQuery]) -> User:
tg_user: telebot.types.User = msg.from_user
if tg_user.id == self.me.id and msg.chat.id > 0:
@@ -93,9 +105,12 @@ class Bot(telebot.TeleBot):
self.db.users.insert_one(user.dict())
return user
- def save(self, user: User):
+ def save_user(self, user: User):
self.db.users.replace_one({'user_id': user.user_id}, user.dict())
+ def save_community(self, community: Community):
+ self.db.communities.replace_one({'chat_id': community.chat_id}, community.dict())
+
def handle_commands(self, cmds: typing.List[str]):
cmds = [edp.replace_colons(cmd) for cmd in cmds]
@@ -111,7 +126,10 @@ class Bot(telebot.TeleBot):
break
args = cmd.join(args.split(cmd)[1:]).strip()
break
- func(msg, user, args)
+ try:
+ func(msg, user, args)
+ except:
+ print(traceback.format_exc())
if cmds[0].startswith('/'):
self.message_handler(commands=[cmd[1:] for cmd in cmds])(func_wrapped)
diff --git a/community.py b/community.py
new file mode 100644
index 0000000..1f43c2d
--- /dev/null
+++ b/community.py
@@ -0,0 +1,57 @@
+from __future__ import annotations
+from dataclasses import dataclass, field, asdict
+import typing
+from datetime import date
+from telebot.types import ChatMember, Chat
+import time
+from threading import Thread
+
+if typing.TYPE_CHECKING:
+ from bot import Bot
+
+
+@dataclass
+class Community:
+ chat_id: int # Telegram chat id
+ name: str = ''
+ members: typing.List[int] = field(default_factory=list)
+ pool: typing.List[typing.Tuple[int, int]] = field(default_factory=list) # (user_id, number_of_copies)
+ scheduled_meetings: typing.List[typing.Tuple[int, int, date]] = field(
+ default_factory=list) # (user_id_1, user_id_2, meeting date)
+ archived_meetings: typing.List[typing.Tuple[int, int, date]] = field(default_factory=list)
+ start_timestamp: int = field(default_factory=lambda: int(time.time()))
+
+ def dict(self) -> dict:
+ data = asdict(self)
+ return data
+
+ @classmethod
+ def from_dict(cls, data: dict) -> Community:
+ data = getattr(data, '__dict__', data)
+ data_ = {key: data[key] for key in data.keys() if
+ key in ['chat_id', 'name', 'members', 'pool', 'scheduled_meetings', 'archived_meetings',
+ 'start_timestamp']}
+ self = cls(**data_)
+ return self
+
+ @classmethod
+ def by_id(cls, chat_id: int, bot: Bot, create_if_not_exists=True) -> typing.Optional[Community]:
+ data = bot.get_community_from_db({'chat_id': chat_id}, create_if_not_exists)
+ if data is None:
+ return None
+
+ def upd_data():
+ chat: Chat = bot.get_chat(chat_id)
+ data.name = chat.title
+ bot.save_community(data)
+
+ Thread(target=upd_data).start()
+ return data
+
+ def add_member(self, user_id: int, bot: Bot) -> bool:
+ chat_member_info: ChatMember = bot.get_chat_member(self.chat_id, user_id)
+ if chat_member_info.is_member or chat_member_info.status in ['creator', 'administrator', 'admin']:
+ self.members.append(user_id)
+ bot.save_community(self)
+ return True
+ return False
diff --git a/main.py b/main.py
index e23c56d..c2ed24b 100644
--- a/main.py
+++ b/main.py
@@ -5,8 +5,8 @@ import traceback
config = {
'mongo': __import__('os').getenv('DB'),
- 'name': 'test_bot',
- 'token': '1686277528:AAHHJgWfulqd9uGmK-RVOM-vQ60kbGgZRIg',
+ 'name': 'ranteabot',
+ 'token': __import__('os').getenv('TOKEN'),
'template_files': ['templates.txt'],
'main_keyboard': {'en': [['ℹ️ About']], 'ru': [['ℹ️ О боте']]}
}
diff --git a/templates.txt b/templates.txt
index 1167203..1bbf197 100644
--- a/templates.txt
+++ b/templates.txt
@@ -16,6 +16,25 @@ Hello, {{ tg_user.first_name }}!
{% for key in stats.keys() %}{{ key }}: {{ stats[key] }}
{% endfor %}
+||<| welcome |>||
+//| en |//
+Thank you for adding RandomTea to your community!
+Now, the members of your community that wish to participate can press the button below👇.
+>>| Join :-: https://t.me/ranteabot?start=comm-{{ community.chat_id }} |<<
+
+//| ru |//
+{# todo #}
+Спасибо за добавление RandomTea в вашу сообщество!
+Теперь пользователи вашего сообщества могут нажать на кнопку ниже👇, чтобы присоединиться к RandomTea.
+>>| Присоединиться :-: https://t.me/ranteabot?start=comm-{{ community.chat_id }} |<<
+
+||<| community_added |>||
+//| en |//
+{# todo: name #}
+You have joined the community, congrats!
+//| ru |//
+Вы присоединились к сообществу, хорошего дня!
+
||<| canceled |>||
//| en |//
Canceled
diff --git a/user.py b/user.py
index 61e9a16..5bde030 100644
--- a/user.py
+++ b/user.py
@@ -1,6 +1,5 @@
from __future__ import annotations
from dataclasses import dataclass, field, asdict
-import attr
import typing
import time
@@ -21,7 +20,8 @@ class User:
@classmethod
def from_dict(cls, data: dict) -> User:
data = getattr(data, '__dict__', data)
- data_ = {key: data[key] for key in data.keys() if key in ['user_id', 'chat_id', 'locale']}
+ data_ = {key: data[key] for key in data.keys() if
+ key in ['user_id', 'chat_id', 'locale', 'start_timestamp', 'last_action_timestamp']}
self = cls(**data_)
return self
diff --git a/utils.py b/utils.py
index edc297b..9a12ef5 100644
--- a/utils.py
+++ b/utils.py
@@ -18,7 +18,7 @@ def isint(st: str) -> bool:
def create_inline_button(text: str, callback_data: str) -> telebot.types.InlineKeyboardButton:
if callback_data.strip().startswith('https://') or callback_data.startswith('tg://'):
- return telebot.types.InlineKeyboardButton(text, url=callback_data)
+ return telebot.types.InlineKeyboardButton(text, url=callback_data.strip())
if callback_data.strip().startswith('inline://'):
telebot.types.InlineKeyboardButton(text,
switch_inline_query_current_chat=callback_data.replace('inline://', ''))
diff --git a/views.py b/views.py
index 515df68..520e167 100644
--- a/views.py
+++ b/views.py
@@ -1,9 +1,22 @@
from bot import Bot
+from community import Community
def views(bot: Bot):
@bot.handle_commands(['/start'])
- def handle_start(msg, _user, _args):
+ def handle_start(msg, user, args):
+ if msg.chat.type in 'supergroup':
+ bot.reply_with_template(msg, 'welcome', community=Community.by_id(msg.chat.id, bot))
+ return
+ if args.strip().startswith('comm'):
+ community_id = int('-'.join(args.strip().split('-')[1:]))
+ community = Community.by_id(community_id, bot)
+ if user.user_id in community.members:
+ bot.reply_with_template(msg, 'community_added', community=community, already_member=True)
+ elif community.add_member(user.user_id, bot):
+ bot.reply_with_template(msg, 'community_added', community=community, already_member=False)
+ else:
+ bot.reply_with_template(msg, 'err_not_a_member', community=community)
bot.reply_with_template(msg, 'start')
@bot.handle_commands(['/help', 'ℹ️ About', 'ℹ️ О боте'])
@@ -13,7 +26,8 @@ def views(bot: Bot):
@bot.handle_commands(['/send_all'])
def send_spam(msg, user, _args):
if not user.is_admin():
- return
+ return
+
def handler(msg_1):
bot.send_all_copy(msg_1)
@@ -32,3 +46,10 @@ def views(bot: Bot):
bot.clear_step_handler_by_chat_id(msg.chat.id)
bot.reply_with_template(msg, 'canceled')
+ @bot.my_chat_member_handler()
+ def handle_my_chat_member(upd):
+ chat_id: int = upd.chat.id
+ # get the community and add it if it doesn't exist
+ community: Community = Community.by_id(chat_id, bot)
+ # send welcome message to the chat (if we can) - and if we should
+ bot.reply_with_template(upd, 'welcome', community=community)