Skip to content

Instantly share code, notes, and snippets.

@sash13
Last active August 30, 2019 15:37
Show Gist options
  • Save sash13/c8eb64a1ffd8e6c07c8e270996436219 to your computer and use it in GitHub Desktop.
Save sash13/c8eb64a1ffd8e6c07c8e270996436219 to your computer and use it in GitHub Desktop.
1. Install Python 3
2. pip install -r requirements.txt
3. Dwnload kyivcityapi.py from https://gist.github.com/sash13/7b5a7aef7e99ec2029b818715a7df766
4. Check Api by run with login pass from id.kyivcity
$python kyivcityapi.py login pass
5. Create bot in telegramm channel @BotFather and get token for bot
6. Fill T_TOKEN from your bot and LOGIN and PASS from id.kyivcity.gov.ua
7. Run script
$python t_kyiv_bot.py
8. Send /start and you see in console chad_id. Copy this number in field USER_ID.
For preventing other users working with your bot.
9. Cntrl+C and start bot again
10. Subscribe for updates /set 60 for updating every 60 secounds. another command:
/hist,/last,/cards,/stats,/unset
python-telegram-bot==11.1.0
requests
lxml
cssselect
pytz
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from telegram.ext import Updater, CommandHandler
from telegram import ParseMode
import logging
from functools import partial
from kyivcityapi import KyivCityAPI
import pytz
import datetime
import sys
#Fill this
T_TOKEN = '' #telegram token
LOGIN = '' # +380111111111
PASS =''
USER_ID = 1111111 # user ID of chat
#
last_time = 'None'
card_id = ''
trip_count = 0
# Enable logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
level=logging.INFO)
logger = logging.getLogger(__name__)
def check_chat(user_id):
return user_id == USER_ID
def parse_date(d):
timeUTC = datetime.datetime.strptime(d, "%Y-%m-%dT%H:%M:%S.%fZ")
timezoneLocal = pytz.timezone('Europe/Kiev')
utc = pytz.utc
return utc.localize(timeUTC).astimezone(timezoneLocal)
def print_date(d):
return parse_date(d).strftime("%Y-%m-%d %H:%M")
def parse_trip(d, time = False):
format_data = ' *' + print_date(d['date']).split(' ')[-1] + '* '
tta = ['Тролейбус', 'Автобус', 'Трамвай']
if 'Платформа' in d['travelType']:
return format_data + ' _Метро_ ' + d['stopName'] +'\n'
if d['travelType'] in tta:
return format_data + ' _' + d['travelType'] + ' №' + str(d['travelLine']) + '_ на ' + d['stopName'] +'\n'
return format_data + ' _Что-то_' + str(d['stopName']) +'\n'
def parse_trips(ds):
format_line = ''
last_time =''
for d in ds:
date = '*' + print_date(d['date']).split(' ')[0] + '*'
if date not in last_time:
format_line += date + '\n'
last_time = date
format_line += parse_trip(d)
return format_line
def parse_pays(ds):
format_line = ''
for d in ds:
format_line += '*' + print_date(d['date']) + '* '
format_line += ( "_Придбання 'Одноразові поїздки'" if d['productType'] in "O" else "_Поповнення гаманця")+ '_ '
format_line += ' ' + str(int(round(d['price']/100.0))) + ' ₴' + '\n'
return format_line
def trips_counter(card):
ticket_count = 0
for asset in card['assets']['purchases']:
ticket_count += asset['residualUnits']
return ticket_count
def start(bot, update):
print(update.message.chat_id, type(update.message.chat_id))
if not check_chat(update.message.chat_id):
update.message.reply_text('Пока недоступно')
return
update.message.reply_text('Привет! Тут что-то будет.')
def stats(bot, update, api):
if not check_chat(update.message.chat_id):
update.message.reply_text('Пока недоступно')
return
STEP = 20
d = api.get_history(card_id, size = STEP)
page = 1
while page*STEP < api.history_total:
page+=1
d += api.get_history(card_id, size = STEP , page = page)
cost = 0
for p in d:
cost += 800-p['price']
cost = cost/100.0
travels = [a['travelType'] for a in d]
occurence = {t:travels.count(t) for t in set(travels)}
print(occurence)
occurence['Платформа'] += occurence['Платформа / Вестибюль']
occurence.pop('Платформа / Вестибюль')
out = sorted(list(occurence.items()), key =
lambda kv:(kv[1], kv[0]), reverse=True)
format_line = '*Всего поездок:* ' + str(api.history_total) + '\n'
#out['Платформа'] += out['Платформа / Вестибюль']
#out.pop('Платформа / Вестибюль')
for o in out:
name = o[0] if 'Платформа' not in o[0] else 'Метро'
format_line += '*' + name + '*: _'+str(o[1])+ 'раз_ ' + str(int(round(float(o[1])/api.history_total*100))) + '%\n'
format_line += '*Сэкономлено:* ' + str(cost) + 'грн'
bot.send_message(chat_id=update.message.chat_id,
text=format_line,
parse_mode=ParseMode.MARKDOWN)
def history(bot, update, api):
if not check_chat(update.message.chat_id):
update.message.reply_text('Пока недоступно')
return
d = api.get_pays(card_id, size = 10)
bot.send_message(chat_id=update.message.chat_id,
text=parse_pays(d),
parse_mode=ParseMode.MARKDOWN)
def last(bot, update, api):
if not check_chat(update.message.chat_id):
update.message.reply_text('Пока недоступно')
return
d = api.get_history(card_id, size = 10)
bot.send_message(chat_id=update.message.chat_id,
text=parse_trips(d),
parse_mode=ParseMode.MARKDOWN)
def cards(bot, update, api):
if not check_chat(update.message.chat_id):
update.message.reply_text('Пока недоступно')
return
format_line = ''
cards_data = api.get_cards()
for card in cards_data:
if card['type'] in 'ridango':
format_line += '*Проездной: ' + card['name'] +'*\n'
format_line += '*Код*: ' + card['code'] +'\n'
format_line += '*Активен*: ' + ('Нет' if card['discounted'] else 'Да') +'\n'
if card['type'] in 'qr':
format_line += '*Проездной: QR билеты*\n'
#format_line += '*Создан*: ' + parse_date(card['createdAt']).strftime("%Y-%m-%d %H:%M") +'\n'
ticket_count = trips_counter(card)
format_line += '*Поездок*: ' + str(ticket_count) +'\n\n'
bot.send_message(chat_id=update.message.chat_id,
text=format_line,
parse_mode=ParseMode.MARKDOWN)
def alarm(bot, job):
if not check_chat(job.context["update"].message.chat_id):
update.message.reply_text('Пока недоступно')
return
global last_time
"""Send the alarm message."""
last = job.context["api"].get_history(job.context["card_id"])[0]
card_data = job.context["api"].get_cards()
global trip_count
trip_count_ = trips_counter(card_data[0])
if trip_count != trip_count_:
print(trip_count_, ' trips')
trip_count =trip_count_
bot.send_message(chat_id=job.context['update'].message.chat_id,
text='Поездка списана. Осталось '+str(trip_count))
if last_time not in last['date'] and last['travelType']:
print(last_time, last)
last_time = last['date']
#bot.send_message(job.context["chat_id"], text=parse_trip(last))
bot.send_message(chat_id=job.context["update"].message.chat_id,
text=parse_trip(last),
parse_mode=ParseMode.MARKDOWN)
def set_timer(bot, update, api, args, job_queue, chat_data):
if not check_chat(update.message.chat_id):
update.message.reply_text('Пока недоступно')
return
"""Add a job to the queue."""
chat_id = update.message.chat_id
try:
# args[0] should contain the time for the timer in seconds
due = int(args[0])
if due < 0:
update.message.reply_text('Sorry we can not go back to future!')
return
global card_id
print(card_id)
# Add job to queue
#job = job_queue.run_once(alarm, due, context=chat_id)
job = job_queue.run_repeating(alarm, due, context={
"chat_id": chat_id,
"api":api,
"card_id": card_id,
"update": update
}
, first=0)
chat_data['job'] = job
update.message.reply_text('Обновление начато!')
except (IndexError, ValueError):
update.message.reply_text('Usage: /set <seconds>')
def unset(bot, update, chat_data):
if not check_chat(update.message.chat_id):
update.message.reply_text('Пока недоступно')
return
"""Remove the job if the user changed their mind."""
if 'job' not in chat_data:
update.message.reply_text('You have no active timer')
return
job = chat_data['job']
job.schedule_removal()
del chat_data['job']
update.message.reply_text('Timer successfully unset!')
def error(bot, update, error):
"""Log Errors caused by Updates."""
logger.warning('Update "%s" caused error "%s"', update, error)
def main():
# Run api
api = KyivCityAPI(LOGIN, PASS)
api.login()
global card_id
card_id = api.get_cards()[0]['id']
"""Run bot."""
updater = Updater(T_TOKEN)
# Get the dispatcher to register handlers
dp = updater.dispatcher
# on different commands - answer in Telegram
dp.add_handler(CommandHandler("start", start))
dp.add_handler(CommandHandler("help", start))
dp.add_handler(CommandHandler("hist", partial(history, api=api)))
dp.add_handler(CommandHandler("last", partial(last, api=api)))
dp.add_handler(CommandHandler("cards", partial(cards, api=api)))
dp.add_handler(CommandHandler("stats", partial(stats, api=api)))
dp.add_handler(CommandHandler("set", partial(set_timer, api=api),
pass_args=True,
pass_job_queue=True,
pass_chat_data=True))
dp.add_handler(CommandHandler("unset", unset, pass_chat_data=True))
# log all errors
dp.add_error_handler(error)
# Start the Bot
updater.start_polling()
# Block until you press Ctrl-C or the process receives SIGINT, SIGTERM or
# SIGABRT. This should be used most of the time, since start_polling() is
# non-blocking and will stop the bot gracefully.
updater.idle()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment