From 44d6729bdac3dadc949f9b0a6879d65417b576bc Mon Sep 17 00:00:00 2001 From: AYAN-AMBESH Date: Thu, 17 Oct 2024 20:07:45 +0530 Subject: [PATCH 1/3] Added gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ceb386 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +venv From 1db11c6b27a442589859cd082eedecdcd2ac55fa Mon Sep 17 00:00:00 2001 From: AYAN-AMBESH Date: Thu, 17 Oct 2024 20:09:06 +0530 Subject: [PATCH 2/3] Updated Code --- README.md | 17 ++- ilugd.py | 373 +++++++++++++++++++++++++++++++---------------- requirements.txt | 5 +- 3 files changed, 259 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index 491a371..4eb9037 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,17 @@ # ilugd-bot + Bot for India Linux User Group Delhi -## Author -* Manas Kashyap (about.me/manaskashyap) +example used from https://github.com/python-telegram-bot/python-telegram-bot/blob/master/examples/chatmemberbot.py + +## Previous Author + +- Manas Kashyap + +## Current Author + +- JustTulpa + +## OG Contributor -## Contributor -* xeon-zolt +- xeon-zolt diff --git a/ilugd.py b/ilugd.py index f4b12d7..b30bb31 100644 --- a/ilugd.py +++ b/ilugd.py @@ -1,141 +1,256 @@ -from telegram.ext import Updater, CommandHandler, MessageHandler, Filters +#!/usr/bin/env python +# pylint: disable=unused-argument +# This program is dedicated to the public domain under the CC0 license. + +""" +Simple Bot to handle '(my_)chat_member' updates. +Greets new users & keeps track of which chats the bot is in. + +Usage: +Press Ctrl-C on the command line or send a signal to the process to stop the +bot. +""" + import configparser import logging -from telegram import ChatAction,ParseMode -from telegram.ext.dispatcher import run_async -from random import choice import os +from typing import Optional, Tuple + +from telegram import Chat, ChatMember, ChatMemberUpdated, Update +from telegram.constants import ParseMode +from telegram.ext import ( + Application, + ChatMemberHandler, + CommandHandler, + ContextTypes, + MessageHandler, + filters, +) -BOTNAME = 'ILUGDbot' +# Enable logging -@run_async -def send_async(bot, *args, **kwargs): - bot.sendMessage(*args, **kwargs) -logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',level=logging.DEBUG) +logging.basicConfig( + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO +) config = configparser.ConfigParser() -config.read('bot.ini') - - -updater = Updater(os.environ['token']) # we should use env variable !! -dispatcher = updater.dispatcher - - -def start(bot, update): - bot.sendChatAction(chat_id=update.message.chat_id, action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text= ''' -Hey!! I'm currently Working with Ilug-Delhi To hire me contact my admin -Use /help to get help''') - - -def website(bot, update): - bot.sendChatAction(chat_id=update.message.chat_id, - action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text=config['BOT']['website']) - -def facebok(bot, update): - bot.sendChatAction(chat_id=update.message.chat_id, - action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text=config['BOT']['facebook']) - -def invitelink(bot, update): - bot.sendChatAction(chat_id=update.message.chat_id, - action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text=config['BOT']['invite_link']) - -def mailinglist(bot,update): - bot.sendChatAction(chat_id=update.message.chat_id, - action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text=config['BOT']['mailinglist']) - -def twitter(bot,update): - bot.sendChatAction(chat_id=update.message.chat_id, - action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text=config['BOT']['twitter']) - -def meetuplink(bot,update): - bot.sendChatAction(chat_id=update.message.chat_id, - action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text=config['BOT']['meetuplink']) - -def github(bot,update): - bot.sendChatAction(chat_id=update.message.chat_id, - action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text=config['BOT']['github']) - -def coc(bot,update): - bot.sendChatAction(chat_id=update.message.chat_id, - action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text=config['BOT']['coc']) - - -def help(bot, update): - bot.sendChatAction(chat_id=update.message.chat_id, action=ChatAction.TYPING) - bot.sendMessage(chat_id=update.message.chat_id, text='''Use one of the following commands -/invitelink - to get Ilug-D Telegram group invite link -/facebook - to get a link to Ilug-D Facebook page +config.read("bot.ini") + +# set higher logging level for httpx to avoid all GET and POST requests being logged +logging.getLogger("httpx").setLevel(logging.WARNING) + +logger = logging.getLogger(__name__) + +tokenid = os.environ["TOKEN"] + + +def extract_status_change( + chat_member_update: ChatMemberUpdated, +) -> Optional[Tuple[bool, bool]]: + """Takes a ChatMemberUpdated instance and extracts whether the 'old_chat_member' was a member + of the chat and whether the 'new_chat_member' is a member of the chat. Returns None, if + the status didn't change. + """ + status_change = chat_member_update.difference().get("status") + old_is_member, new_is_member = chat_member_update.difference().get( + "is_member", (None, None) + ) + + if status_change is None: + return None + + old_status, new_status = status_change + was_member = old_status in [ + ChatMember.MEMBER, + ChatMember.OWNER, + ChatMember.ADMINISTRATOR, + ] or (old_status == ChatMember.RESTRICTED and old_is_member is True) + is_member = new_status in [ + ChatMember.MEMBER, + ChatMember.OWNER, + ChatMember.ADMINISTRATOR, + ] or (new_status == ChatMember.RESTRICTED and new_is_member is True) + + return was_member, is_member + + +async def track_chats(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + """Tracks the chats the bot is in.""" + result = extract_status_change(update.my_chat_member) + if result is None: + return + was_member, is_member = result + + # Let's check who is responsible for the change + cause_name = update.effective_user.full_name + + # Handle chat types differently: + chat = update.effective_chat + if chat.type == Chat.PRIVATE: + if not was_member and is_member: + # This may not be really needed in practice because most clients will automatically + # send a /start command after the user unblocks the bot, and start_private_chat() + # will add the user to "user_ids". + # We're including this here for the sake of the example. + logger.info("%s unblocked the bot", cause_name) + context.bot_data.setdefault("user_ids", set()).add(chat.id) + elif was_member and not is_member: + logger.info("%s blocked the bot", cause_name) + context.bot_data.setdefault("user_ids", set()).discard(chat.id) + elif chat.type in [Chat.GROUP, Chat.SUPERGROUP]: + if not was_member and is_member: + logger.info("%s added the bot to the group %s", cause_name, chat.title) + context.bot_data.setdefault("group_ids", set()).add(chat.id) + elif was_member and not is_member: + logger.info("%s removed the bot from the group %s", cause_name, chat.title) + context.bot_data.setdefault("group_ids", set()).discard(chat.id) + elif not was_member and is_member: + logger.info("%s added the bot to the channel %s", cause_name, chat.title) + context.bot_data.setdefault("channel_ids", set()).add(chat.id) + elif was_member and not is_member: + logger.info("%s removed the bot from the channel %s", cause_name, chat.title) + context.bot_data.setdefault("channel_ids", set()).discard(chat.id) + + +async def show_chats(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + """Shows which chats the bot is in""" + user_ids = ", ".join( + str(uid) for uid in context.bot_data.setdefault("user_ids", set()) + ) + group_ids = ", ".join( + str(gid) for gid in context.bot_data.setdefault("group_ids", set()) + ) + channel_ids = ", ".join( + str(cid) for cid in context.bot_data.setdefault("channel_ids", set()) + ) + text = ( + f"@{context.bot.username} is currently in a conversation with the user IDs {user_ids}." + f" Moreover it is a member of the groups with IDs {group_ids} " + f"and administrator in the channels with IDs {channel_ids}." + ) + await update.effective_message.reply_text(text) + + +async def greet_chat_members( + update: Update, context: ContextTypes.DEFAULT_TYPE +) -> None: + """Greets new users in chats and announces when someone leaves""" + result = extract_status_change(update.chat_member) + if result is None: + return + + was_member, is_member = result + cause_name = update.chat_member.from_user.mention_html() + member_name = update.chat_member.new_chat_member.user.mention_html() + + if not was_member and is_member: + await update.effective_chat.send_message( + "Hello {member_name}! Welcome to Ilug-D .let's start with introduction, was added by {cause_name}.", + parse_mode=ParseMode.HTML, + ) + elif was_member and not is_member: + await update.effective_chat.send_message( + f"{member_name} is no longer with us. Thanks a lot, {cause_name} ...", + parse_mode=ParseMode.HTML, + ) + + +async def start_private_chat( + update: Update, context: ContextTypes.DEFAULT_TYPE +) -> None: + """Greets the user and records that they started a chat with the bot if it's a private chat. + Since no `my_chat_member` update is issued when a user starts a private chat with the bot + for the first time, we have to track it explicitly here. + """ + user_name = update.effective_user.full_name + chat = update.effective_chat + if chat.type != Chat.PRIVATE or chat.id in context.bot_data.get("user_ids", set()): + return + + logger.info("%s started a private chat with the bot", user_name) + context.bot_data.setdefault("user_ids", set()).add(chat.id) + + await update.effective_message.reply_text( + f"Welcome {user_name}. Use /show_chats to see what chats I'm in." + ) + + +async def help(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + text = """Use one of the following commands +/invite - to get Ilug-D Telegram group invite link /website - to get Ilug-D website link /mailinglist - link for our mailing list /twitter - twitter link for ILUGD -/meetuplink - to get meetup link for ILUGD +/meetup - to get meetup link for ILUGD /github - link to ilugd github repos -''') - - -# Welcome a user to the chat -def welcome(bot, update): - message = update.message - chat_id = message.chat.id - phrases = ['Hello {}! Welcome to {} .Please introduce yourself. Dont forget to read the chat rules using !rules command'.format(message.new_chat_member.first_name,message.chat.title), - 'Hi {}! Welcome to {} .let\'s start with introduction. Dont forget to read the chat rules using !rules command'.format(message.new_chat_member.first_name,message.chat.title) - #'Hello {}! Welcome to {} .Please introduce yourself.'.format(message.new_chat_member.first_name,message.chat.title), - #'Hello {}! Welcome to {} .Please introduce yourself.'.format(message.new_chat_member.first_name,message.chat.title), - #'Hello {}! Welcome to {} .Please introduce yourself.'.format(message.new_chat_member.first_name,message.chat.title) - ] - text = choice(phrases) - send_async(bot, chat_id=chat_id, text=text, parse_mode=ParseMode.HTML) - - -def goodbye(bot, update): - message = update.message - chat_id = message.chat.id - text = 'Goodbye, $username!' - text = text.replace('$username',message.left_chat_member.first_name).replace('$title', message.chat.title) - send_async(bot, chat_id=chat_id, text=text, parse_mode=ParseMode.HTML) - -def intro(bot, update): - message = update.message - chat_id = message.chat.id - text = 'Hi everyone,I am a python bot working to serve Ilug-D.' - send_async(bot, chat_id=chat_id, text=text, parse_mode=ParseMode.HTML) - -def empty_message(bot, update): - - if update.message.new_chat_member is not None: - # Bot was added to a group chat - if update.message.new_chat_member.username == BOTNAME: - return intro(bot, update) - # Another user joined the chat - else: - return welcome(bot, update) - - # Someone left the chat - elif update.message.left_chat_member is not None: - if update.message.left_chat_member.username != BOTNAME: - return goodbye(bot, update) - - -dispatcher.add_handler(CommandHandler('website', website)) -dispatcher.add_handler(CommandHandler('facebook', facebok)) -dispatcher.add_handler(CommandHandler('help', help)) -dispatcher.add_handler(MessageHandler([Filters.status_update], empty_message)) -dispatcher.add_handler(CommandHandler('invitelink',invitelink)) -dispatcher.add_handler(CommandHandler('mailinglist',mailinglist)) -dispatcher.add_handler(CommandHandler('twitter',twitter)) -dispatcher.add_handler(CommandHandler('meetuplink',meetuplink)) -dispatcher.add_handler(CommandHandler('start', start)) -dispatcher.add_handler(CommandHandler('github',github)) - -updater.start_polling() -updater.idle() +""" + await update.effective_message.reply_text(text) + + +async def website(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + text = cpnfig["BOT"]["website"] + await update.effective_message.reply_text(text) + + +async def invitelink(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + text = config["BOT"]["invite_link"] + await update.effective_message.reply_text(text) + + +async def mailinglist(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + text = config["BOT"]["mailinglist"] + await update.effective_message.reply_text(text) + + +async def twitter(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + text = config["BOT"]["twitter"] + await update.effective_message.reply_text(text) + + +async def meetup(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + text = config["BOT"]["meetuplink"] + await update.effective_message.reply_text(text) + + +async def github(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: + text = config["BOT"]["github"] + await update.effective_message.reply_text(text) + + +def main() -> None: + """Start the bot.""" + # Create the Application and pass it your bot's token. + application = Application.builder().token(tokenid).build() + + # Keep track of which chats the bot is in + application.add_handler( + ChatMemberHandler(track_chats, ChatMemberHandler.MY_CHAT_MEMBER) + ) + application.add_handler(CommandHandler("show_chats", show_chats)) + + application.add_handler(CommandHandler("help", help)) + application.add_handler(CommandHandler("invite", invitelink)) + application.add_handler( + CommandHandler("mailinglist", mailinglist) + ) # Handle members joining/leaving chats. + application.add_handler(CommandHandler("twitter", twitter)) + application.add_handler(CommandHandler("github", github)) + application.add_handler(CommandHandler("meetup", meetup)) + + application.add_handler( + ChatMemberHandler(greet_chat_members, ChatMemberHandler.CHAT_MEMBER) + ) + + # Interpret any other command or text message as a start of a private chat. + # This will record the user as being in a private chat with bot. + application.add_handler(MessageHandler(filters.ALL, start_private_chat)) + + # Run the bot until the user presses Ctrl-C + # We pass 'allowed_updates' handle *all* updates including `chat_member` updates + # To reset this, simply pass `allowed_updates=[]` + application.run_polling(allowed_updates=Update.ALL_TYPES) + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt index f0036f0..bc35fa8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ -python-telegram-bot==5.3.0 -requests==2.20.0 -configparser==3.5.0 \ No newline at end of file +python-telegram==0.19.0 +telegram_text==0.2.0 From f273b41ebb79cd429f966d2a1cab40286ed68592 Mon Sep 17 00:00:00 2001 From: AYAN-AMBESH Date: Thu, 17 Oct 2024 20:13:28 +0530 Subject: [PATCH 3/3] corrected reuqirements.txt --- requirements.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index bc35fa8..28f1207 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,11 @@ -python-telegram==0.19.0 +anyio==4.6.2.post1 +certifi==2024.8.30 +exceptiongroup==1.2.2 +h11==0.14.0 +httpcore==1.0.6 +httpx==0.27.2 +idna==3.10 +python-telegram-bot==21.6 +sniffio==1.3.1 telegram_text==0.2.0 +typing_extensions==4.12.2