Skip to content

Commit

Permalink
Merge pull request #9 from macisamuele/make-repository-installable-as…
Browse files Browse the repository at this point in the history
…-python-package

Update bot to support Python3, make it installable as python package
  • Loading branch information
domenicoblanco committed Oct 2, 2018
2 parents ef25287 + 8f044e8 commit d3022aa
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 91 deletions.
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
config.py
__pycache__
*.egg
*.egg-info/
*.pyc
.idea/
.telegram.token
.vscode/
__pycache__
venv/
.gitignore
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
include LICENSE
include README.md
19 changes: 7 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ To get starting developing, we really recommend to clone the bot locally and sta
### Linux
1. Install [Python 2.7](https://www.python.org/) with your package manager.
```
Debian
# Debian
sudo apt install python-2.7
```

Expand All @@ -24,27 +24,22 @@ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py
```

3. Install [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot)
3. Install [gdg-pisa-user-manager](https://github.com/gdgpisa/gdgpisausermanager)
```
Debian
pip install python-telegram-bot --upgrade
```
Or you can install from source with
```
git clone https://github.com/python-telegram-bot/python-telegram-bot --recursive
cd python-telegram-bot
python setup.py install
pip install git+https://github.com/gdgpisa/gdgpisausermanager --upgrade
# If the command above fails, try with
pip install https://github.com/gdgpisa/gdgpisausermanager/archive/master.zip --upgrade
```

4. Start a chat with [BotFather](https://t.me/BotFather)

5. Use the ```/newbot``` command to create a new bot. The BotFather will ask you for a name and username, then generate an authorization token for your new bot.

6. Copy the token and paste it on the variable **TOKEN** inside the [python script](https://github.com/gdgpisa/gdgpisausermanager/blob/master/config.py#L2)
6. Copy the token into `.telegram.token` file. The content will be read by [`Config.TOKEN`](https://github.com/gdgpisa/gdgpisausermanager/blob/master/config.py#L7)

7. Run
```
python gdgpisausermanager.py
gdg-pisa-user-manager
```

## Contributing 🤝
Expand Down
25 changes: 22 additions & 3 deletions config.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
class Config(object):
TOKEN = ""
WAITING_TIMEOUT = 30.0
# -*- coding: utf-8 -*-
import os


class _Config(object):
@property
def TOKEN(self):
try:
with open('.telegram.token') as f:
return ''.join(f.readlines()).strip()
except:
raise RuntimeError(
'Telegram token not set. Please make sure that you followed the instructions on README and that you '
'have created the token file in `{}` and that is readable from the current user'.format(
os.path.join(os.path.abspath('.'), '.telegram.token')
),
)

WAITING_TIMEOUT = 30.0


Config = _Config()
199 changes: 126 additions & 73 deletions gdgpisausermanager.py
Original file line number Diff line number Diff line change
@@ -1,138 +1,191 @@
# -*- coding: utf-8 -*-
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters, CallbackQueryHandler, ConversationHandler
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, ParseMode
from config import Config
import datetime
import time
import os
import random
import threading
from __future__ import unicode_literals

import logging
import threading
from sys import maxsize

from telegram import InlineKeyboardButton
from telegram import InlineKeyboardMarkup
from telegram import ParseMode
from telegram.error import NetworkError
from telegram.ext import CallbackQueryHandler
from telegram.ext import CommandHandler
from telegram.ext import Filters
from telegram.ext import MessageHandler
from telegram.ext import Updater

from config import Config

logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s',
level=logging.INFO)

logging.basicConfig(
format='%(asctime)s - %(levelname)s - %(message)s',
level=logging.INFO,
)

logger = logging.getLogger(__name__)

new_users = []


def welcome(bot, update):
for new_user_obj in update.message.new_chat_members:
try:
new_user = "@" + new_user_obj['username']
reply_text = "Benvenut*"+new_user+"! Premi <i>Confermo</i> per dimostrare di non essere un bot." \
.format(new_user_obj.username, new_user, update.message.chat.title)
except Exception as _:
new_user = new_user_obj['first_name']
reply_text = "Benvenut*"+new_user+"! Premi <i>Confermo</i> per dimostrare di non essere un bot." \
.format(new_user, update.message.chat.title)
logger.info("Nuovo utente: {}".format(new_user))
"""
:type bot: telegram.Bot
:type update: telegram.Update
"""
for new_user_obj in update.message.new_chat_members: # new_user_obj has telegram.user.User type
user_handle = new_user_obj.name
user_id = new_user_obj.id
chat_id = update.message.chat_id

reply_text = "Benvenuto/a {}! Premi <i>Confermo</i> per dimostrare di non essere un bot.".format(
user_handle,
)

new_users.append(new_user_obj.id)

keyboard = [[InlineKeyboardButton("Confermo", callback_data=1)]]
reply_markup = InlineKeyboardMarkup(keyboard)

mexid = bot.send_message(
update.message.chat_id,
message = bot.send_message(
chat_id=update.message.chat_id,
text=reply_text,
reply_markup=reply_markup,
parse_mode=ParseMode.HTML
parse_mode=ParseMode.HTML,
)

threading.Timer(Config.WAITING_TIMEOUT, timer,
[bot, new_user_obj.id, update.message.chat_id, mexid.message_id]).start()
return
threading.Timer(
interval=Config.WAITING_TIMEOUT,
function=timer,
args=[bot, user_id, chat_id, message.message_id],
).start() # TODO: keep track of the timer to cancel it if the user confirmed it's identity

logger.info("Nuovo utente: {}.".format(user_handle))


def button_pressed(bot, update):
"""
:type bot: telegram.Bot
:type update: telegram.Update
"""
global new_users

if update.callback_query.from_user.id in new_users:
user_handle = update.callback_query.from_user.name
chat_id = update.callback_query.message.chat_id
message_id = update.callback_query.message.message_id

new_users.remove(update.callback_query.from_user.id)
bot.deleteMessage(update.callback_query.message.chat_id, update.callback_query.message.message_id)
bot.delete_message(chat_id, message_id=message_id)

try:
user = "@" + update.callback_query.from_user.username
except Exception as _:
user = update.callback_query.from_user.first_name
bot.send_message(
chat_id,
text="{}: grazie per aver completato la registrazione!".format(user_handle),
)

bot.send_message(update.callback_query.message.chat_id,
text=user + ", grazie per aver completato la registrazione!")
logger.info("Utente confermato: {}.".format(user_handle))


def check_activity(bot, update):
"""
:type bot: telegram.Bot
:type update: telegram.Update
"""
bot.send_message(
chat_id=update.message.chat_id,
text="pong",
reply_to_message_id=update.message.message_id)
reply_to_message_id=update.message.message_id,
)


def is_admin(bot, userid, chatid):
return userid in [admin.user.id for admin in bot.get_chat_administrators(chatid)]
def is_admin(bot, user_id, chat_id):
"""
:type bot: telegram.Bot
:type user_id: str or int
:type chat_id: str or int
"""
try:
return any(user_id == admin.user.id for admin in bot.get_chat_administrators(chat_id))
except NetworkError:
return False


def ban_user(bot, update):
"""
:type bot: telegram.Bot
:type update: telegram.Update
"""
if update.message.reply_to_message is None:
return

if is_admin(bot, update.message.from_user.id, update.message.chat.id):
if not is_admin(bot, update.message.reply_to_message.from_user.id, update.message.chat.id):
reply_text = "{} è stato bannato con successo.".format(update.message.reply_to_message.from_user.username)
bot.kickChatMember(update.message.chat.id, update.message.reply_to_message.from_user.id)
bot.send_message(update.message.chat.id, text=reply_text)
chat_id = update.message.chat.id

try:
user = "@" + update.message.reply_to_message.from_user.username
except Exception as _:
user = update.message.reply_to_message.from_user.first_name
if is_admin(bot, user_id=update.message.from_user.id, chat_id=chat_id):
user_id = update.message.reply_to_message.from_user.id
user_handle = update.message.reply_to_message.from_user.name

logger.info("The user {} was banned with success".format(user))
if not is_admin(bot, user_id, chat_id):
bot.kick_chat_member(chat_id, user_id)
bot.send_message(
chat_id,
text="{} è stato bannato con successo.".format(user_handle),
)

logger.info("Utente {} bannato con successo.".format(user_handle))


def kick_user(bot, update):
"""
:type bot: telegram.Bot
:type update: telegram.Update
"""
# FIXME: not clear what is the difference with respect to ban_user

if update.message.reply_to_message is None:
return

if is_admin(bot, update.message.from_user.id, update.message.chat.id):
if not is_admin(bot, update.message.reply_to_message.from_user.id, update.message.chat.id):
bot.kickChatMember(update.message.chat.id, update.message.reply_to_message.from_user.id)
bot.unbanChatMember(update.message.chat.id, update.message.reply_to_message.from_user.id)
chat_id = update.message.chat.id

try:
user = "@" + update.message.reply_to_message.from_user.username
except Exception as _:
user = update.message.reply_to_message.from_user.first_name
if is_admin(bot, user_id=update.message.from_user.id, chat_id=chat_id):
user_id = update.message.reply_to_message.from_user.id
user_handle = update.message.reply_to_message.from_user.name

logger.info("The user {} was kicked with success".format(user))
if is_admin(bot, user_id, chat_id):
bot.kick_chat_member(chat_id, user_id)
bot.unban_chat_member(chat_id, user_id)

logger.info("The user {} was kicked with success.".format(user_handle))


def main():
updater = Updater(Config.TOKEN)
dp = updater.dispatcher
dp.add_handler(MessageHandler(Filters.status_update.new_chat_members, welcome))
dp.add_handler(MessageHandler(Filters.regex('^ping$'), check_activity))
dp.add_handler(CallbackQueryHandler(button_pressed))
dp.add_handler(CommandHandler('ban', ban_user))
dp.add_handler(CommandHandler('kick', kick_user))
updater.dispatcher.add_handler(MessageHandler(Filters.status_update.new_chat_members, welcome))
updater.dispatcher.add_handler(MessageHandler(Filters.regex('^ping$'), check_activity))
updater.dispatcher.add_handler(CallbackQueryHandler(button_pressed))
updater.dispatcher.add_handler(CommandHandler('ban', ban_user))
updater.dispatcher.add_handler(CommandHandler('kick', kick_user))

updater.start_polling()
updater.idle()

try:
updater.idle()
except KeyboardInterrupt:
print("Chiusura programma")


def timer(bot, userid, chatid, mexid):
def timer(bot, user_id, chat_id, message_id):
"""
:type bot: telegram.Bot
:type user_id: str or int
:type chat_id: str or int
:type message_id: str or int
"""
global new_users
if userid in new_users:
logger.info("User with id: {} auto-removed with success".format(userid))
bot.kickChatMember(chatid, userid, until_date=25)
bot.deleteMessage(chatid, mexid)
if user_id in new_users:
logger.info("User with id: {} auto-removed with success".format(user_id))
bot.kick_chat_member(chat_id, user_id, until_date=maxsize) # Kicking a member for over 365 days is forever
bot.delete_message(chat_id, message_id=message_id)
return


try:
if __name__ == '__main__':
main()
except KeyboardInterrupt:
print("Chiusura programma")
if __name__ == '__main__':
exit(main())
6 changes: 6 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[metadata]
description-file = README.md
license_file = LICENSE

[wheel]
universal = True
34 changes: 34 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2011-2018, Yelp, Inc.
import os

from setuptools import setup

setup(
name='gdg-pisa-user-manager',
version='1.0.0',
license='MIT License',
description='Bot for managing the Telegram group of GDG Pisa',
long_description=open(os.path.join(os.path.dirname(__file__), 'README.md')).read(),
author='GDG Pise',
author_email='TODO',
url='https://github.com/gdgpisa/gdgpisausermanager/',
py_modules=['gdgpisausermanager'],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Topic :: Communications :: Chat',
'Topic :: Internet',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
],
install_requires=['python-telegram-bot'],
entry_points={'console_scripts': ['gdg-pisa-user-manager = gdgpisausermanager:main']},
)

0 comments on commit d3022aa

Please sign in to comment.