support context based callback, fix bugs, add cards system

This commit is contained in:
JerryXiao 2019-10-15 18:51:46 +08:00
parent 864c72f6ce
commit ae5c7b6eb5
Signed by: Jerry
GPG key ID: 9D9CE43650FF2BAA
4 changed files with 372 additions and 64 deletions

288
cards.py Normal file
View file

@ -0,0 +1,288 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from telegram import InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import run_async
from data import get_player
from random import randrange
from time import time
MAX_LEVEL: int = 100
MID_LEVEL: int = 80
LVL_UP_CARDS: int = 20
def display_username(user, atuser=True, shorten=False, markdown=True):
"""
atuser and shorten has no effect if markdown is True.
"""
name = user.full_name
if markdown:
mdtext = user.mention_markdown(name=user.full_name)
return mdtext
if shorten:
return name
if user.username:
if atuser:
name += " (@{})".format(user.username)
else:
name += " ({})".format(user.username)
return name
def _msg_users(update):
'''
get from_user and reply_to_user
'''
if update.message:
if update.message.reply_to_message:
return (update.message.from_user,
update.message.reply_to_message.from_user)
else:
return (update.message.from_user, None)
else:
return (None, None)
@run_async
def getperm(update, context):
(from_user, reply_to_user) = _msg_users(update)
if not from_user:
return
if reply_to_user:
tuser = reply_to_user
else:
tuser = from_user
tplayer = get_player(int(tuser.id))
update.message.reply_text(f"{display_username(tuser)} 等级为 {tplayer.permission}",
parse_mode="Markdown")
@run_async
def setperm(update, context):
(from_user, reply_to_user) = _msg_users(update)
if not from_user:
return
if reply_to_user:
if context.args and len(context.args) == 1:
try:
new_level = int(context.args[0])
except ValueError:
update.message.reply_text('数字不合法')
return
else:
update.message.reply_text('请指定新的等级')
return
if get_player(int(from_user.id)).permission >= MAX_LEVEL:
tplayer = get_player(int(reply_to_user.id))
tplayer.permission = new_level
tplayer.save()
update.message.reply_text('请求成功')
else:
update.message.reply_text('请求忽略')
else:
update.message.reply_text('请回复被操作人')
@run_async
def lvlup(update, context):
'''
use LVL_UP_CARDS cards to level up 1 lvl
'''
(from_user, reply_to_user) = _msg_users(update)
if not from_user:
return
if reply_to_user:
fplayer = get_player(int(from_user.id))
tplayer = get_player(int(reply_to_user.id))
if fplayer.immunity_cards >= LVL_UP_CARDS:
fplayer.immunity_cards -= LVL_UP_CARDS
if tplayer.permission <= MAX_LEVEL - 2 or tplayer.permission >= MAX_LEVEL:
tplayer.permission += 1
fplayer.save()
tplayer.save()
update.message.reply_text((f"{display_username(from_user)} 消耗了{LVL_UP_CARDS}张免疫卡,"
f"{display_username(reply_to_user)} 升了1级"),
parse_mode="Markdown")
else:
update.message.reply_text(f"您的免疫卡不足({fplayer.immunity_cards}){LVL_UP_CARDS}张免疫卡兑换1等级",
parse_mode="Markdown")
else:
fplayer = get_player(int(from_user.id))
if fplayer.immunity_cards >= LVL_UP_CARDS:
fplayer.immunity_cards -= LVL_UP_CARDS
if fplayer.permission <= MAX_LEVEL - 2 or fplayer.permission >= MAX_LEVEL:
fplayer.permission += 1
fplayer.save()
update.message.reply_text((f"{display_username(from_user)} 消耗了{LVL_UP_CARDS}张免疫卡,"
"为 自己 升了1级"), parse_mode="Markdown")
else:
update.message.reply_text(f"您的免疫卡不足({fplayer.immunity_cards}){LVL_UP_CARDS}张免疫卡兑换1等级",
parse_mode="Markdown")
@run_async
def transfer_cards(update, context):
(from_user, reply_to_user) = _msg_users(update)
if not from_user:
return
if reply_to_user:
if context.args and len(context.args) == 1:
try:
amount = int(context.args[0])
except ValueError:
update.message.reply_text('数字不合法')
return
else:
update.message.reply_text('请指定数量')
return
if from_user.id == reply_to_user.id:
fplayer = get_player(int(from_user.id))
if fplayer.permission >= MID_LEVEL:
fplayer.immunity_cards += amount
fplayer.save()
update.message.reply_text(f'{display_username(from_user)} 转给了自己{amount}张卡', parse_mode="Markdown")
else:
update.message.reply_text(f'{display_username(from_user)} 转给了自己{amount}张卡', parse_mode="Markdown")
else:
fplayer = get_player(int(from_user.id))
tplayer = get_player(int(reply_to_user.id))
if (amount >= 0 and fplayer.immunity_cards >= amount) or \
(fplayer.permission >= MID_LEVEL and tplayer.permission <= fplayer.permission):
fplayer.immunity_cards -= amount
tplayer.immunity_cards += amount
fplayer.save()
tplayer.save()
update.message.reply_text(f'{display_username(from_user)} 转给了 {display_username(from_user)} {amount}张卡',
parse_mode="Markdown")
else:
update.message.reply_text(f'转账失败,你可能没有这么多卡哦({fplayer.immunity_cards}/{amount})',
parse_mode="Markdown")
else:
update.message.reply_text('请回复被操作人')
@run_async
def rob_cards(update, context):
ROB_TIMEOUT = 10
last_time = context.user_data.setdefault('rob_time', 0.0)
ctime = time()
if ctime - last_time < ROB_TIMEOUT:
update.message.reply_text('别急,你不是刚刚才来过吗')
return
else:
context.user_data['rob_time'] = ctime
(from_user, reply_to_user) = _msg_users(update)
if not from_user:
return
if reply_to_user:
amount = randrange(1, 9)
if from_user.id == reply_to_user.id:
fplayer = get_player(int(from_user.id))
fplayer.immunity_cards -= amount
fplayer.save()
update.message.reply_text(f'{display_username(from_user)} 自己抢走自己{amount}张卡', parse_mode="Markdown")
else:
fplayer = get_player(int(from_user.id))
tplayer = get_player(int(reply_to_user.id))
_fp = fplayer.permission if fplayer.permission > 0 else 0
_tp = tplayer.permission if tplayer.permission > 0 else 0
success_chance = _fp / (_fp + _tp) if _fp + _tp != 0 else 0.5
def __chance(percentage):
if randrange(0,10000)/10000 < percentage:
return True
else:
return False
if __chance(success_chance):
msg_text = "抢劫成功,获得"
else:
msg_text = "抢劫失败,反被抢走"
amount = -amount
fplayer.immunity_cards += amount
tplayer.immunity_cards -= amount
fplayer.save()
tplayer.save()
update.message.reply_text(f'{display_username(from_user)} {msg_text}{abs(amount)}张卡', parse_mode="Markdown")
else:
update.message.reply_text('请回复被操作人')
@run_async
def cards_lottery(update, context):
LOTTERY_TIMEOUT = 10
last_time = context.user_data.setdefault('lottery_time', 0.0)
ctime = time()
if ctime - last_time < LOTTERY_TIMEOUT:
update.message.reply_text('别急,你不是刚刚才来过吗')
return
else:
context.user_data['lottery_time'] = ctime
(from_user, _) = _msg_users(update)
if not from_user:
return
fplayer = get_player(int(from_user.id))
cards = abs(fplayer.immunity_cards) / 3
def __floating(value):
return randrange(5000,15000)/10000 * value
cards = __floating(cards)
cards = int(cards) if cards > 1 else 1
cards *= randrange(-1, 2, 2)
fplayer.immunity_cards += cards
update.message.reply_text(f'{"获得" if cards >= 0 else "血亏"}{abs(cards)}张卡')
@run_async
def dist_cards(update, context):
(from_user, _) = _msg_users(update)
if not from_user:
return
try:
if context.args and len(context.args) == 2:
(cards, damount) = [int(a) for a in context.args]
assert (cards > 0 and damount > 0)
fplayer = get_player(int(from_user.id))
fplayer.immunity_cards -= cards
fplayer.save()
red_packets = context.chat_data.setdefault('red_packets', dict())
rphash = str(hash(f"{update.effective_chat.id} {update.effective_message.message_id}"))[:8]
red_packets[rphash] = [cards, damount]
update.message.reply_text(f'{display_username(from_user)}的红包🧧', parse_mode="Markdown",
reply_markup=InlineKeyboardMarkup.from_button(
InlineKeyboardButton(text=f"{cards} / {damount}",
callback_data=f"dist {rphash}")
))
else:
raise ValueError('')
except (ValueError, AssertionError):
update.message.reply_text(f'数字不合法: /dist 卡 红包数量')
@run_async
def dist_cards_btn_click(update, context):
data = update.callback_query.data
user = update.callback_query.from_user
omsg = update.callback_query.message
try:
(_, rphash) = data.split(' ')
red_packets = context.chat_data.setdefault('red_packets', dict())
rp = red_packets.get(str(rphash), None)
if rp:
(cards, damount) = [int(a) for a in rp]
assert (cards > 0 and damount > 0)
def __floating(value):
return randrange(5000,15000)/10000 * value
got_cards = int(__floating(cards/damount))
got_cards = got_cards if got_cards <= cards else cards
got_cards = got_cards if damount != 1 else cards
rp[0] -= got_cards
rp[1] -= 1
(cards, damount) = rp
fplayer = get_player(int(user.id))
fplayer.immunity_cards += cards
fplayer.save()
update.callback_query.answer(text=f"你得到了{got_cards}张卡", show_alert=False)
if cards > 0 and damount > 0:
omsg.reply_markup.inline_keyboard[0][0].text = f"{cards} / {damount}"
omsg.edit_reply_markup(reply_markup=omsg.reply_markup)
else:
raise AssertionError('')
else:
raise AssertionError('')
except (ValueError, AssertionError):
try:
update.callback_query.answer()
except Exception:
pass
omsg.edit_text(omsg.text_markdown + "褪裙了", parse_mode="Markdown", reply_markup=None)

11
data.py
View file

@ -13,25 +13,18 @@ class Player(Model):
wins = IntegerField() wins = IntegerField()
restricted_until = IntegerField() restricted_until = IntegerField()
immunity_cards = IntegerField() immunity_cards = IntegerField()
permission = IntegerField()
class Meta: class Meta:
database = db database = db
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
db.close()
@staticmethod
def db_close():
db.close()
db.connect() db.connect()
db.create_tables([Player]) db.create_tables([Player])
db.close()
def get_player(user_id): def get_player(user_id):
db.connect()
player = Player.get_or_none(Player.user_id == user_id) player = Player.get_or_none(Player.user_id == user_id)
if player is None: if player is None:
player = Player.create(user_id=user_id, mines=0, death=0, wins=0, player = Player.create(user_id=user_id, mines=0, death=0, wins=0,
restricted_until=0, immunity_cards=0) restricted_until=0, immunity_cards=0, permission=0)
return player return player
else: else:
return player return player

View file

@ -1,28 +1,27 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
db = lambda _=None: None
setattr(db, 'close', lambda _=None: None)
pool = dict() pool = dict()
class Player(): class Player():
def __init__(self, user_id, mines, death, wins, restricted_until, immunity_cards): def __init__(self, user_id, mines, death, wins,
restricted_until, immunity_cards, permission):
self.user_id = user_id self.user_id = user_id
self.mines = mines self.mines = mines
self.death = death self.death = death
self.wins = wins self.wins = wins
self.restricted_until = restricted_until self.restricted_until = restricted_until
self.immunity_cards = immunity_cards self.immunity_cards = immunity_cards
@staticmethod self.permission = permission
def save():
pass
@staticmethod
def db_close():
pass
def get_player(user_id): def get_player(user_id):
player = pool.get(user_id, None) player = pool.get(user_id, None)
if player is None: if player is None:
player = Player(user_id=user_id, mines=0, death=0, wins=0, player = Player(user_id=user_id, mines=0, death=0, wins=0,
restricted_until=0, immunity_cards=0) restricted_until=0, immunity_cards=0, permission=0)
pool[user_id] = player pool[user_id] = player
return player return player
else: else:

View file

@ -6,9 +6,10 @@ from telegram import InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, run_async from telegram.ext import Updater, CommandHandler, CallbackQueryHandler, run_async
from telegram.error import TimedOut as TimedOutError from telegram.error import TimedOut as TimedOutError
from numpy import array_equal from numpy import array_equal
# If no peewee orm is installed, try `from data_ram import get_player` # If no peewee orm is installed, try `from data_ram import get_player, db`
from data import get_player from data import get_player, db
from random import randint, choice from random import randint, choice, randrange
from math import log
from threading import Lock from threading import Lock
import time import time
import logging import logging
@ -17,7 +18,7 @@ logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(name)s - %(leveln
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
token = "token_here" token = "token_here"
updater = Updater(token, workers=8) updater = Updater(token, workers=8, use_context=True)
job_queue = updater.job_queue job_queue = updater.job_queue
job_queue.start() job_queue.start()
@ -29,8 +30,9 @@ WIDTH = 8
MINES = 9 MINES = 9
UNOPENED_CELL = "\u2588" UNOPENED_CELL = "\u2588"
FLAGGED_CELL = "\u259a" FLAGGED_CELL = "\U0001f6a9"
STEPPED_CELL = "*" #FLAGGED_CELL = "\u259a"
STEPPED_CELL = "\u2622"
WIN_TEXT_TEMPLATE = "哇所有奇怪的地方都被你打开啦…好羞羞\n" \ WIN_TEXT_TEMPLATE = "哇所有奇怪的地方都被你打开啦…好羞羞\n" \
"地图Op {s_op} / Is {s_is} / 3bv {s_3bv}\n操作总数 {ops_count}\n" \ "地图Op {s_op} / Is {s_is} / 3bv {s_3bv}\n操作总数 {ops_count}\n" \
@ -132,13 +134,16 @@ game_manager = GameManager()
@run_async @run_async
def send_keyboard(bot, update, args): def send_keyboard(update, context):
(bot, args) = (context.bot, context.args)
msg = update.message msg = update.message
logger.info("Mine from {0}".format(update.message.from_user.id)) logger.info("Mine from {0}".format(update.message.from_user.id))
if check_restriction(update.message.from_user): if check_restriction(update.message.from_user):
update.message.reply_text("爆炸这么多次还想扫雷?") update.message.reply_text("爆炸这么多次还想扫雷?")
return return
# create a game board # create a game board
if args is None:
args = list()
if len(args) == 3: if len(args) == 3:
height = HEIGHT height = HEIGHT
width = WIDTH width = WIDTH
@ -182,64 +187,74 @@ def send_keyboard(bot, update, args):
bot.send_message(chat_id=msg.chat.id, text="路过的大爷~来扫个雷嘛~", reply_to_message_id=msg.message_id, bot.send_message(chat_id=msg.chat.id, text="路过的大爷~来扫个雷嘛~", reply_to_message_id=msg.message_id,
parse_mode="Markdown", reply_markup=InlineKeyboardMarkup(keyboard)) parse_mode="Markdown", reply_markup=InlineKeyboardMarkup(keyboard))
def send_help(bot, update): def send_help(update, context):
logger.debug("Start from {0}".format(update.message.from_user.id)) logger.debug("Start from {0}".format(update.message.from_user.id))
msg = update.message msg = update.message
msg.reply_text("这是一个扫雷bot\n\n/mine 开始新游戏") msg.reply_text("这是一个扫雷bot\n\n/mine 开始新游戏")
def send_source(bot, update): def send_source(update, context):
logger.debug("Source from {0}".format(update.message.from_user.id)) logger.debug("Source from {0}".format(update.message.from_user.id))
update.message.reply_text('Source code: https://git.jerryxiao.cc/Jerry/tgmsbot') update.message.reply_text('Source code: https://git.jerryxiao.cc/Jerry/tgmsbot')
def send_status(bot, update): def send_status(update, context):
logger.info("Status from {0}".format(update.message.from_user.id)) logger.info("Status from {0}".format(update.message.from_user.id))
count = game_manager.count() count = game_manager.count()
update.message.reply_text('当前进行的游戏: {}'.format(count)) update.message.reply_text('当前进行的游戏: {}'.format(count))
def gen_reward(user, negative=True): def gen_reward(user, negative=True):
''' Reward the player :) ''' ''' Reward the player :) '''
def __chance(percentage):
if randrange(0,10000)/10000 < percentage:
return True
else:
return False
def __floating(value):
return randrange(8000,12000)/10000 * value
def __lose_cards(cardnum):
if cardnum <= 6:
return 1
else:
return int(__floating(log(cardnum, 2)))
def __get_cards(cardnum):
if cardnum >= 2:
cards = __floating(1 / log(cardnum, 100))
if cards > 1.0:
return int(cards)
else:
return int(__chance(cards))
else:
return int(__floating(8.0))
# Negative rewards # Negative rewards
def restrict_mining(player): def restrict_mining(player):
if player.immunity_cards >= 1: lost_cards = __lose_cards(player.immunity_cards)
if player.immunity_cards >= 10: player.immunity_cards -= lost_cards
lost_cards = randint(2,4) if player.immunity_cards >= 0:
elif player.immunity_cards >= 5:
lost_cards = randint(1,3)
else:
lost_cards = 1
player.immunity_cards -= lost_cards
ret = "用去{}张免疫卡,还剩{}".format(lost_cards, player.immunity_cards) ret = "用去{}张免疫卡,还剩{}".format(lost_cards, player.immunity_cards)
else: else:
now = int(time.time()) now = int(time.time())
seconds = randint(30, 120) seconds = randint(30, 120)
player.restricted_until = now + seconds player.restricted_until = now + seconds
ret = "没有免疫卡了,被限制扫雷{}".format(seconds) ret = "没有免疫卡了,被限制扫雷{}".format(seconds)
player.save()
return ret return ret
# Positive rewards # Positive rewards
def give_immunity_cards(player): def give_immunity_cards(player):
rewarded_cards = 0 rewarded_cards = __get_cards(player.immunity_cards)
if player.immunity_cards <= 3:
rewarded_cards = randint(1, 2)
elif player.immunity_cards <= 10:
if randint(1, 5) == 5:
rewarded_cards = 1
elif randint(1, 10) == 10:
rewarded_cards = 1
player.immunity_cards += rewarded_cards player.immunity_cards += rewarded_cards
player.save()
if rewarded_cards == 0: if rewarded_cards == 0:
return "共有{}张免疫卡".format(player.immunity_cards) return "共有{}张免疫卡".format(player.immunity_cards)
else: else:
return "被奖励了{}张免疫卡,共有{}".format(rewarded_cards, player.immunity_cards) return "被奖励了{}张免疫卡,共有{}".format(rewarded_cards, player.immunity_cards)
player = get_player(user.id) player = get_player(user.id)
if negative: try:
player.death += 1 if negative:
return restrict_mining(player) player.death += 1
else: return restrict_mining(player)
player.wins += 1 else:
return give_immunity_cards(player) player.wins += 1
return give_immunity_cards(player)
finally:
player.save()
def game_count(user): def game_count(user):
player = get_player(user.id) player = get_player(user.id)
@ -248,7 +263,6 @@ def game_count(user):
def check_restriction(user): def check_restriction(user):
player = get_player(user.id) player = get_player(user.id)
player.db_close()
now = int(time.time()) now = int(time.time())
if now >= player.restricted_until: if now >= player.restricted_until:
return False return False
@ -256,11 +270,10 @@ def check_restriction(user):
return player.restricted_until - now return player.restricted_until - now
@run_async @run_async
def player_statistics(bot, update): def player_statistics(update, context):
logger.info("Statistics from {0}".format(update.message.from_user.id)) logger.info("Statistics from {0}".format(update.message.from_user.id))
user = update.message.from_user user = update.message.from_user
player = get_player(user.id) player = get_player(user.id)
player.db_close()
mines = player.mines mines = player.mines
death = player.death death = player.death
wins = player.wins wins = player.wins
@ -271,7 +284,7 @@ def player_statistics(bot, update):
wins=wins, cards=cards)) wins=wins, cards=cards))
def update_keyboard_request(bot, bhash, game, chat_id, message_id): def update_keyboard_request(context, bhash, game, chat_id, message_id):
current_action_timestamp = time.time() current_action_timestamp = time.time()
if current_action_timestamp - game.last_action <= KBD_MIN_INTERVAL: if current_action_timestamp - game.last_action <= KBD_MIN_INTERVAL:
logger.debug('Rate limit triggered.') logger.debug('Rate limit triggered.')
@ -280,8 +293,9 @@ def update_keyboard_request(bot, bhash, game, chat_id, message_id):
context=(bhash, game, chat_id, message_id, current_action_timestamp)) context=(bhash, game, chat_id, message_id, current_action_timestamp))
else: else:
game.last_action = current_action_timestamp game.last_action = current_action_timestamp
update_keyboard(bot, None, noqueue=(bhash, game, chat_id, message_id)) update_keyboard(context, noqueue=(bhash, game, chat_id, message_id))
def update_keyboard(bot, job, noqueue=None): def update_keyboard(context, noqueue=None):
(bot, job) = (context.bot, context.job)
if noqueue: if noqueue:
(bhash, game, chat_id, message_id) = noqueue (bhash, game, chat_id, message_id) = noqueue
else: else:
@ -317,7 +331,8 @@ def update_keyboard(bot, job, noqueue=None):
game.timeouts += 1 game.timeouts += 1
@run_async @run_async
def handle_button_click(bot, update): def handle_button_click(update, context):
bot = context.bot
msg = update.callback_query.message msg = update.callback_query.message
user = update.callback_query.from_user user = update.callback_query.from_user
chat_id = update.callback_query.message.chat.id chat_id = update.callback_query.message.chat.id
@ -340,10 +355,10 @@ def handle_button_click(bot, update):
if game is None: if game is None:
logger.debug("No game found for hash {}".format(bhash)) logger.debug("No game found for hash {}".format(bhash))
return return
game.lock.acquire()
try: try:
if game.stopped: if game.stopped:
return return
game.lock.acquire()
board = game.board board = game.board
if board.state == 0: if board.state == 0:
mmap = None mmap = None
@ -355,7 +370,7 @@ def handle_button_click(bot, update):
game.lock.release() game.lock.release()
game.save_action(user, (row, col)) game.save_action(user, (row, col))
if not array_equal(board.map, mmap): if not array_equal(board.map, mmap):
update_keyboard_request(bot, bhash, game, chat_id, msg.message_id) update_keyboard_request(context, bhash, game, chat_id, msg.message_id)
(s_op, s_is, s_3bv) = board.gen_statistics() (s_op, s_is, s_3bv) = board.gen_statistics()
ops_count = game.actions_sum() ops_count = game.actions_sum()
ops_list = game.get_actions() ops_list = game.get_actions()
@ -394,7 +409,7 @@ def handle_button_click(bot, update):
elif mmap is None or (not array_equal(board.map, mmap)): elif mmap is None or (not array_equal(board.map, mmap)):
game.lock.release() game.lock.release()
game.save_action(user, (row, col)) game.save_action(user, (row, col))
update_keyboard_request(bot, bhash, game, chat_id, msg.message_id) update_keyboard_request(context, bhash, game, chat_id, msg.message_id)
else: else:
game.lock.release() game.lock.release()
except: except:
@ -405,12 +420,25 @@ def handle_button_click(bot, update):
raise raise
from cards import getperm, setperm, lvlup, transfer_cards, rob_cards, cards_lottery, dist_cards, dist_cards_btn_click
updater.dispatcher.add_handler(CommandHandler('getlvl', getperm))
updater.dispatcher.add_handler(CommandHandler('setlvl', setperm))
updater.dispatcher.add_handler(CommandHandler('lvlup', lvlup))
updater.dispatcher.add_handler(CommandHandler('transfer', transfer_cards))
updater.dispatcher.add_handler(CommandHandler('rob', rob_cards))
updater.dispatcher.add_handler(CommandHandler('lottery', cards_lottery))
updater.dispatcher.add_handler(CommandHandler('dist', dist_cards))
updater.dispatcher.add_handler(CallbackQueryHandler(dist_cards_btn_click, pattern=r'dist'))
updater.dispatcher.add_handler(CommandHandler('start', send_help)) updater.dispatcher.add_handler(CommandHandler('start', send_help))
updater.dispatcher.add_handler(CommandHandler('mine', send_keyboard, pass_args=True)) updater.dispatcher.add_handler(CommandHandler('mine', send_keyboard))
updater.dispatcher.add_handler(CommandHandler('status', send_status)) updater.dispatcher.add_handler(CommandHandler('status', send_status))
updater.dispatcher.add_handler(CommandHandler('stats', player_statistics)) updater.dispatcher.add_handler(CommandHandler('stats', player_statistics))
updater.dispatcher.add_handler(CommandHandler('source', send_source)) updater.dispatcher.add_handler(CommandHandler('source', send_source))
updater.dispatcher.add_handler(CallbackQueryHandler(handle_button_click)) updater.dispatcher.add_handler(CallbackQueryHandler(handle_button_click))
updater.start_polling() try:
updater.idle() updater.start_polling()
updater.idle()
finally:
db.close()