From cf0fd570fd928e9638ccc2d8d5628a0fe848689d Mon Sep 17 00:00:00 2001 From: Marco Date: Sun, 1 Jan 2017 16:51:00 +0100 Subject: [PATCH 1/2] Orario treni 3.1 - Aggiunto il tracciamento dei treni - Aggiunti i grafici dei ritardo dei treni - Migliorati gli arrivi e le partenze delle stazioni - Abilitato il logging per prevenire flood in chat del bot - Bug fixes --- API.py | 197 +++++++++++++++++++++++++++++++++------ Bot.py | 29 +++++- CONFIG.py | 8 ++ Callback/Callback.py | 5 +- Callback/Generale.py | 33 ++++++- Callback/Tracciamento.py | 47 ++++++++++ Callback/Treni.py | 66 +++++++++++++ Inlinemode/Inline.py | 2 + Tracciamento.py | 128 +++++++++++++++++++++++++ 9 files changed, 480 insertions(+), 35 deletions(-) create mode 100644 Callback/Tracciamento.py create mode 100644 Tracciamento.py diff --git a/API.py b/API.py index 6d04094..719b80b 100644 --- a/API.py +++ b/API.py @@ -1,9 +1,30 @@ import urllib.request import json import sqlite3 +import time from datetime import datetime import datetime from urllib import * +import os + +import random +import string + +import logging +logger = logging.getLogger("bot") +logging.getLogger("requests").setLevel(logging.WARNING) + +format = "%(asctime)s [%(levelname)s]: %(message)s" +level = logging.INFO +logging.basicConfig(format=format, level=level) + +from CONFIG import PLOTLY_USERNAME, PLOTLY_API_KEY + +import plotly +plotly.tools.set_credentials_file(username=PLOTLY_USERNAME, api_key=PLOTLY_API_KEY) + +import plotly.plotly as py +import plotly.graph_objs as go conn = sqlite3.connect('OrarioTreni.db') c = conn.cursor() @@ -13,7 +34,7 @@ class db: def creaTutto(): """Crea la la connessione e la table""" - conn = sqlite3.connect('OrarioTreniBot.db') + conn = sqlite3.connect('OrarioTreni.db') c = conn.cursor() try: c.execute('''CREATE TABLE stato(userid INTEGER, stato STRING, completato INTEGER)''') @@ -30,6 +51,11 @@ def creaTutto(): except: pass + try: + c.execute('''CREATE TABLE tracciamento(request_id INTEGER, userid INTEGER, id_treno TEXT, solo_oggi BOOLEAN, stazione_ultimo_rilevamento TEXT, random_string TEXT)''') + except: + pass + conn.commit() def updateState(userid, new_state, completato): @@ -37,6 +63,7 @@ def updateState(userid, new_state, completato): c.execute('''DELETE FROM stato WHERE userid=?''',(userid,)) c.execute('''INSERT INTO stato VALUES(?,?,?)''',(userid, new_state, completato)) conn.commit() + logger.info("Utente {} nuovo stato {}".format(userid, new_state)) return True, None #return except Exception as e: return False, e @@ -46,6 +73,7 @@ def getState(userid): c.execute('''SELECT stato, completato FROM stato WHERE userid=?''',(userid,)) rows = c.fetchall() for res in rows: + logger.debug("Stato dell'utente {}: {} {}".format(userid, res[0], res[1])) return res[0], res[1], True, None #return conn.commit() except Exception as e: @@ -55,6 +83,37 @@ def resetItinerario(userid): c.execute('''DELETE FROM itinerario WHERE userid=?''',(userid,)) conn.commit() + def tracciaTreno(user_id, id_treno, solo_oggi): + data, success, error = orarioTreni.cercaTreno(id_treno) + + stazione_ultimo_rilevamento = data['stazioneUltimoRilevamento'] + if stazione_ultimo_rilevamento == "--": + stazione_ultimo_rilevamento = data['origine'] + + if stazione_ultimo_rilevamento == data['destinazione']: + logger.debug("Utente {} ha provato a tracciare il treno {} ma è già arrivato a destinazione".format(user_id, id_treno)) + return "Il treno è già arrivato a destinazione, traccialo domani!" + + random_string = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase) for _ in range(10)) + + c.execute('SELECT * FROM tracciamento WHERE userid=? AND id_treno=?', (user_id, id_treno,)) + if c.fetchall(): + logger.debug("Utente {} ha provato a tracciare il treno {} ma lo stava già tracciando".format(user_id, id_treno)) + return "Stai già tracciando questo treno!" + + c.execute('''SELECT request_id FROM tracciamento ORDER BY request_id DESC LIMIT 1''') + rows = c.fetchall() + if not rows: + request_id = 0 + for res in rows: + request_id = res[0] + 1 + + c.execute('''INSERT INTO tracciamento VALUES(?, ?, ?, ?, ?, ?)''', (request_id, user_id, id_treno, solo_oggi, stazione_ultimo_rilevamento, random_string)) + conn.commit() + + logger.info("Utente {} ha messo a tracciare il treno {}, request id {}".format(user_id, id_treno, request_id)) + return request_id + class orarioTreni: """Cerca treni, arrivi, partenze, itinerari, statistiche""" def tipo(stringa): @@ -79,9 +138,11 @@ def cercaTreno(id_treno): info = "http://www.viaggiatreno.it/viaggiatrenonew/resteasy/viaggiatreno/andamentoTreno/"+id_stazione+"/"+id_treno response = urllib.request.urlopen(info) except: #errore urllib (non trovato) + logging.debug("Treno {} non trovato".format(id_treno)) return None, False, 404 #data, success, error content = response.read() data = json.loads(content.decode("utf8")) + logging.info("Cercato il treno {}".format(id_treno)) return data, True, None def cercaItinerario(stazione1, stazione2, orario): @@ -122,6 +183,7 @@ def cercaItinerario(stazione1, stazione2, orario): response = urllib.request.urlopen(content) content = response.read() data = json.loads(content.decode("utf8")) + logging.info("Itinerario cercato da {} a {} orario {}".format(stazione1, stazione2, orario)) return data, True, None def cercaStatistiche(): @@ -129,6 +191,7 @@ def cercaStatistiche(): response = urllib.request.urlopen(content) content = response.read() data = json.loads(content.decode("utf8")) + logging.info("Statistiche cercate") return data, True, None class stazione: @@ -138,13 +201,15 @@ def check(stazione): content = "http://www.viaggiatreno.it/viaggiatrenonew/resteasy/viaggiatreno/cercaStazione/"+stazione.replace(" ","%20") #soluzione temporanea TODO response = urllib.request.urlopen(content) except Exception as e: + logging.info("{} non è una stazione".format(stazione)) return False, None content = response.read() if content == b'[]': + logging.info("{} non è una stazione".format(stazione)) return False, None data = json.loads(content.decode("utf8")) - + logging.info("{} è una stazione".format(stazione)) return True, data def informazioni(stazione): @@ -166,6 +231,7 @@ def informazioni(stazione): response = urllib.request.urlopen(content) content = response.read() data = json.loads(content.decode("utf8")) + logging.info("{} informazioni ottenute".format(stazione)) return data @@ -187,6 +253,7 @@ def arrivi(stazione): response = urllib.request.urlopen(content) content = response.read() data = json.loads(content.decode("utf8")) + logging.info("{} arrivi ottenuti".format(stazione)) return data, True, None def partenze(stazione): @@ -207,6 +274,7 @@ def partenze(stazione): response = urllib.request.urlopen(content) content = response.read() data = json.loads(content.decode("utf8")) + logging.info("{} partenze ottenute".format(stazione)) return data, True, None class Messaggi: @@ -215,6 +283,7 @@ def erroreDB(message, error): "\n_Ci scusiamo per il disagio._" "\nInoltra questo messaggio *tecnico* a @MarcoBuster *[DEV]*:" "`{}`".format(error)) + logging.error("Erorre database: {}".format(error)) def treno1(data): orarioPartenza = datetime.datetime.fromtimestamp(data['orarioPartenza'] / 1000).strftime('%H:%M') orarioArrivo = datetime.datetime.fromtimestamp(data['orarioArrivo'] / 1000).strftime('%H:%M') @@ -235,12 +304,13 @@ def treno1(data): .format(data['categoria'], str(data['numeroTreno']), data['origine'], orarioPartenza, data['destinazione'], orarioArrivo, str(data['ritardo']), data['stazioneUltimoRilevamento'], oraUltimoRilevamento, str(n_fermate))) + logging.info("Formattato treno {}".format(data['numeroTreno'])) return testo def arriviStazione(data, nomestazione): messaggio_iniziale = "Arrivi della stazione di "+nomestazione+":\n" - for k in range(0,4): - messaggio = None + messaggio = "" + for k in range(0,9): try: data[k]['numeroTreno'] except IndexError or TypeError: @@ -250,8 +320,8 @@ def arriviStazione(data, nomestazione): if data[k]['inStazione'] == False: inStazione = "No" elif data[k]['inStazione'] == True: - inStazione == "Sì" - messaggio = ("🚅Treno {} {}" + inStazione = "Sì" + messaggio += ("🚅Treno {} {}" "\n🚉Proveniente da: {}" "\n🚧In stazione: {}" "\n🕒Ritardo: {}m" @@ -261,12 +331,13 @@ def arriviStazione(data, nomestazione): if messaggio == None: messaggio = "\nNon c'è nessun treno in arrivo in questa stazione" testo = messaggio_iniziale + messaggio + logging.info("Formattati arrivi stazione {}".format(nomestazione)) return testo def partenzeStazione(data, nomestazione): messaggio_iniziale = "Partenze della stazione di "+nomestazione+":\n" - for k in range(0,4): - messaggio = None + messaggio = "" + for k in range(0,9): try: data[k]['numeroTreno'] except IndexError or TypeError: @@ -276,8 +347,8 @@ def partenzeStazione(data, nomestazione): if data[k]['inStazione'] == False: inStazione = "No" elif data[k]['inStazione'] == True: - inStazione == "Sì" - messaggio = ("🚅Treno {} {}" + inStazione = "Sì" + messaggio += ("🚅Treno {} {}" "\n🚉Diretto a: {}" "\n🚧In stazione: {}" "\n🕒Ritardo: {}m" @@ -287,29 +358,52 @@ def partenzeStazione(data, nomestazione): if messaggio == None: messaggio = "\nNon c'è nessun treno in partenza in questa stazione" testo = messaggio_iniziale + messaggio + logging.info("Formattati arrivi stazione {}".format(nomestazione)) return testo def itinerario(data): - durata = data['soluzioni'][0]['durata'] - messaggio = "Ho trovato questo itinerario da {0} a {1}\n".format(data['origine'], data['destinazione']) - n_cambi = -1 + messaggio = "Ho trovato questo itinerario da {0} a {1}".format(data['origine'], data['destinazione']) inline_keyboard = '[' - for dict in data['soluzioni'][0]['vehicles']: - n_cambi = n_cambi + 1 - orarioPartenza = (datetime.datetime.strptime(dict['orarioPartenza'], '%Y-%m-%dT%H:%M:%S')).strftime('%H:%M') - orarioArrivo = (datetime.datetime.strptime(dict['orarioArrivo'], '%Y-%m-%dT%H:%M:%S')).strftime('%H:%M') - if n_cambi > 0: - a_capo = "\n\n🚧Cambio🚧\n" - else: - a_capo = "" - messaggio = messaggio + a_capo + ( - "🚅Treno {0} {1}" - "\n🚉Parte da {2} alle ore {3}" - "\n🚉Arriva a {4} alle ore {5}".format(dict['categoriaDescrizione'], str(dict['numeroTreno']), dict['origine'], orarioPartenza, dict['destinazione'], orarioArrivo) - ) - inline_keyboard = inline_keyboard + '[{"text":"🔍Altre informazioni sul treno '+dict['categoriaDescrizione']+" "+str(dict['numeroTreno'])+'", "callback_data": "agg@'+str(dict['numeroTreno'])+'"}],' + soluzioni = "" + n_soluzioni = 0 + fff = "" + + for dictionary in data['soluzioni']: + n_soluzioni += 1 + soluzioni = "\n\n➖➖➖Soluzione {n}".format(n=n_soluzioni) + fff += "\n\n\nSoluzione #{n}".format(n=n_soluzioni) + + n_cambi = -1 + + for dict in dictionary['vehicles']: + n_cambi = n_cambi + 1 + fff += "---Cambio #{n}".format(n=n_cambi) + orarioPartenza = datetime.datetime.strptime(dict['orarioPartenza'], '%Y-%m-%dT%H:%M:%S').strftime('%H:%M') + orarioArrivo = datetime.datetime.strptime(dict['orarioArrivo'], '%Y-%m-%dT%H:%M:%S').strftime('%H:%M') + + if n_cambi > 0: + a_capo = "\n🚧Cambio🚧" + else: + a_capo = "" + + if n_cambi == 0: + soluzione = soluzioni + else: + soluzione = "" + + messaggio = messaggio + soluzione + a_capo + ( + "\n🚅Treno {0} {1}" + "\n🚉Parte da {2} alle ore {3}" + "\n🚉Arriva a {4} alle ore {5}".format(dict['categoriaDescrizione'], str(dict['numeroTreno']), dict['origine'], orarioPartenza, dict['destinazione'], orarioArrivo) + ) + inline_keyboard = inline_keyboard + '[{"text":"🔍Altre informazioni sul treno '+dict['categoriaDescrizione']+" "+str(dict['numeroTreno'])+'", "callback_data": "agg@'+str(dict['numeroTreno'])+'"}],' + + if n_soluzioni > 4: + break inline_keyboard = inline_keyboard + '[{"text":"🔙Torna indietro", "callback_data":"home"}]]' + logging.info("Formattato itinerario da {} a {}".format(data['origine'], data['destinazione'])) + print(messaggio) return messaggio, inline_keyboard def listaStazioni(data): @@ -326,6 +420,7 @@ def listaStazioni(data): messaggio = "Ho trovato {} stazioni con quel nome:".format(numero_dict) inline_keyboard = inline_keyboard + '[{"text":"🔙Torna indietro","callback_data":"home"}]]' + logging.info("Formattata lista stazioni") return messaggio, inline_keyboard def fermata(data, numeroFermata): @@ -395,7 +490,7 @@ def fermata(data, numeroFermata): ritardoArrivo = "con un ritardo di {} minuti".format(str(ritardoArrivo)) emoji = "❗️" elif ritardoArrivo < 1: - ritardoArrivo = "in anticipo di {} minuti".format(str(abs(RitardoArrivo))) + ritardoArrivo = "in anticipo di {} minuti".format(str(abs(ritardoArrivo))) emoji = "⁉️" if data['ritardoArrivo'] == 0: @@ -488,7 +583,9 @@ def fermata(data, numeroFermata): ritardoArrivo = "in perfetto orario" emoji = "👌" - if data['binarioEffettivoArrivoDescrizione'] == None: + if data['binarioEffettivoArrivoDescrizione'] == None and data['binarioProgrammatoArrivoDescrizione'] == None: + binario = "?" + elif data['binarioEffettivoArrivoDescrizione'] == None: binario = data['binarioProgrammatoArrivoDescrizione'].strip() else: binario = data['binarioEffettivoArrivoDescrizione'].strip() @@ -522,6 +619,7 @@ def fermata(data, numeroFermata): "\n\nNon arrabiarti con lo sviluppatore o lasciare recensioni negative, tu non immagini nemmeno quante variabili ci sono in ballo e quanto i dati di Trenitalia siano sballati a volte😢" #A sad but true story "\nGuarda il codice su GitHub, se non ci credi: www.github.com/MarcoBuster/OrarioTreniBot.".format(str(id_treno), str(numeroFermata), str(data['actualFermataType'])) ) + logging.error("Formattazione fermata id treno: {}, numero fermata: {}, actualFermataType: {}".format(id_treno, numeroFermata, actualFermataType)) return messaggio if Arrivo == None and Partenza == None: @@ -529,6 +627,7 @@ def fermata(data, numeroFermata): "ℹ️Informazioni del treno {0} {1} rispetto alla fermata {2}\n".format(cat_treno, id_treno, data['stazione']) +actualFermataType ) + logging.info("Formattazione fermata {} treno {} ".format(numeroFermata, id_treno)) return messaggio else: messaggio = ( @@ -537,12 +636,48 @@ def fermata(data, numeroFermata): +Partenza+ ("\n" if Partenza != "" else "") +actualFermataType ) + logging.info("Formattazione fermata {} treno {} ".format(numeroFermata, id_treno)) return messaggio + def grafico(data, id_treno): + fermate = [] + ritardi = [] + + for dictionary in data['fermate']: + if dictionary['actualFermataType'] == 0: + break + + fermate = fermate + [dictionary['stazione']] + ritardi = ritardi + [dictionary['ritardo']] + + if len(fermate) < 2 or len(ritardi) < 2: + return False + + line = go.Scatter( + x = fermate, + y = ritardi, + name = 'Ritardo', + line = dict( + color = ('rgb(205, 12, 24)'), + width = 4, + shape = 'spline') + ) + + title = 'Ritardo del treno {id_treno} • @OrarioTreniBot'.format(id_treno=id_treno) + layout = dict(title = title, + xaxis = dict(title = 'Fermata'), + yaxis = dict(title = 'Ritardo (minuti)'), + ) + + filename = os.getcwd() + "/ritardo_treno@{id_treno}.png".format(id_treno=id_treno) + fig = dict(data=[line], layout=layout) + py.image.save_as(fig, filename=filename) + return filename + def statistiche(data): messaggio = ("Statistiche dei treni circolanti:" "\n🚅Treni oggi: {}" "\n🚅Treni circolanti in questo momento: {}" - "\n✅Versione del bot: 3.0 OPEN ALPHA".format(str(data['treniGiorno']), str(data['treniCircolanti']))) - + "\n✅Versione del bot: 3.1".format(str(data['treniGiorno']), str(data['treniCircolanti']))) + logging.info("Formattazione statistiche") return messaggio diff --git a/Bot.py b/Bot.py index d2498e1..2f701e4 100644 --- a/Bot.py +++ b/Bot.py @@ -11,6 +11,14 @@ from Inlinemode import Inline from Callback.InlineCallback import INCallback +import logging +logger = logging.getLogger("bot") +logging.getLogger("requests").setLevel(logging.WARNING) + +format = "%(asctime)s [%(levelname)s]: %(message)s" +level = logging.INFO +logging.basicConfig(format=format, level=level) + import botogram.objects.base class CallbackQuery(botogram.objects.base.BaseObject): required = { @@ -81,6 +89,7 @@ def process_inline(bot, chains, update): @bot.command("start") def start(chat, message, args): + logger.info("Utente {} comando /start".format(message.sender.id)) if len(args) == 1: if args[0] == "inline": @@ -141,7 +150,7 @@ def post(chat, message, args): bot.chat(res[0]).send(message) chat.send("Post sent to "+str(res[0])) except botogram.api.ChatUnavailableError: - c.execute('DELETE FROM stato WHERE userid={}'.format(res[0])) + c.execute('DELETE FROM stato WHERE userid=?', (user_id,)) chat.send("The user "+str(res[0])+" has blocked your bot, so I removed him from the database") conn.commit() except Exception as e: @@ -176,6 +185,8 @@ def cerca_treno(chat, message): if state != "treno1": return + logger.info("Utente {} process message cerca treno".format(message.sender.id)) + id_treno = str(message.text) data, success, error = API.orarioTreni.cercaTreno(id_treno) if success == False and error == 404: @@ -193,7 +204,10 @@ def cerca_treno(chat, message): "reply_markup": '{"inline_keyboard":[[{"text":"🚉Lista fermate","callback_data":"'+callbackdata1+'"},' '{"text":"🔄Aggiorna le informazioni","callback_data":"'+callbackdata2+'"}],' + '[{"text": "🚦Traccia il treno [BETA]", "callback_data": "traccia@'+id_treno+'"},' + '{"text": "📊Grafico ritardo", "callback_data": "grafico@'+id_treno+'"}],' '[{"text":"🔙Torna indietro","callback_data":"home"}]]}'}) + API.db.updateState(chat.id, "nullstate", 0) @bot.process_message @@ -203,6 +217,8 @@ def cerca_stazione(chat, message): if state != "stazione1": return + logger.info("Utente {} process message cerca stazione".format(message.sender.id)) + stazione = message.text esiste, data = API.orarioTreni.stazione.check(stazione) @@ -228,6 +244,7 @@ def cerca_stazione(chat, message): '{"inline_keyboard":[[{"text":"Arrivi","callback_data":"'+callbackdata1+'"},{"text":"Partenze","callback_data":"'+callbackdata2+'"}],'\ '[{"text":"📍Posizione","callback_data":"'+callbackdata3+'"}],[{"text":"🔙Torna indietro","callback_data":"home"}]]}'} ) + return bot.api.call("sendMessage", { "chat_id":chat.id, "text":testo, "parse_mode":"HTML", "reply_markup":'{' @@ -242,6 +259,8 @@ def sottoscrivi_itinerario_stazione1(chat, message): #CHIEDE NOME RICEVE USERNAM conn.commit() return + logger.info("Utente {} process message itinerario stazione".format(message.sender.id)) + stazione = message.text esiste, data = API.orarioTreni.stazione.check(stazione) if esiste == False: @@ -291,6 +310,8 @@ def sottoscrivi_itinerario_stazione2(chat, message): #CHIEDE NOME RICEVE USERNAM conn.commit() return + logger.info("Utente {} process message itinerario stazione 2".format(message.sender.id)) + if completato == 0 and state == "itinerario2": c.execute('''UPDATE stato SET completato=1 WHERE userid=?''',(chat.id,)) conn.commit() @@ -340,6 +361,8 @@ def sottoscrivi_itinerario_orario(chat, message): #CHIEDE NOME RICEVE USERNAME conn.commit() return + logger.info("Utente {} process message itinerario orario".format(message.sender.id)) + if completato == 0 and state == "itinerario3": c.execute('''UPDATE stato SET completato=1 WHERE userid=?''',(chat.id,)) conn.commit() @@ -378,6 +401,7 @@ def nullstate(chat, message): @bot.command("feedback") def feedback(chat, message, args): + logger.info("Utente {} comando /feedback".format(message.sender.id)) if len(args) == 0: message.reply("*Comando /feedback*" "\nQuesto comando *invia* direttamente un *feedback* allo *sviluppatore* (@MarcoBuster)" @@ -428,4 +452,5 @@ def no_old_commands(chat, message): return True if __name__ == "__main__": - bot.run() + bot.process_backlog = True + bot.run(workers=5) diff --git a/CONFIG.py b/CONFIG.py index 0d6162f..cae98ea 100644 --- a/CONFIG.py +++ b/CONFIG.py @@ -10,3 +10,11 @@ ''' TOKEN = "INSERT YOUR TOKEN HERE" + +''' +Configurazione di Plotly (per i grafici) +Per avere le credenziali è necessario registrarsi su plot.ly +''' + +PLOTLY_USERNAME = 'PLOTLY USERNAME' +PLOTLY_API_KEY = 'PLOTLY API KEY' diff --git a/Callback/Callback.py b/Callback/Callback.py index ceb0929..ab6984a 100644 --- a/Callback/Callback.py +++ b/Callback/Callback.py @@ -3,14 +3,17 @@ import API import Bot -from Callback import Generale, Treni, Stazioni, Itinerario +from Callback import Generale, Treni, Stazioni, Itinerario, Tracciamento import sqlite3 conn = sqlite3.connect('OrarioTreni.db') c = conn.cursor() def process(bot, chains, update): + Bot.logger.info("Utente {} callback {}".format(update.callback_query.sender.id, update.callback_query.data)) + Generale.callback(bot, chains, update) Treni.callback(bot, chains, update) Stazioni.callback(bot, chains, update) Itinerario.callback(bot, chains, update) + Tracciamento.callback(bot, chains, update) diff --git a/Callback/Generale.py b/Callback/Generale.py index bb8419a..115cc14 100644 --- a/Callback/Generale.py +++ b/Callback/Generale.py @@ -40,6 +40,7 @@ def callback(bot, chains, update): "\n\nLink utili:" "\nCreatore e sviluppatore: 👉 @MarcoBuster" "\nGruppo del bot: 👉 @MarcoBusterGroup" + "\nCanale con aggiornamenti e anteprime: 👉 @OrarioTreni" "\nVota il bot: 👉 Storebot" "\nCodice sorgente: GitHub") @@ -86,9 +87,39 @@ def callback(bot, chains, update): if callback_q == "altro": testo = "Visualizza le *statistiche* o vai al codice sorgente di *GitHub*" bot.api.call("editMessageText", {"chat_id":chat.id, "message_id": message.message_id, "text": testo, "parse_mode":"Markdown", "reply_markup":\ - '{"inline_keyboard":[[{"text":"📊Statistiche","callback_data":"stats"},{"text":"💻GitHub","url":"www.github.com/MarcoBuster/OrarioTreniBot"}],'\ + '{"inline_keyboard":[[{"text":"📊Statistiche","callback_data":"stats"}, {"text":"💻GitHub","url":"www.github.com/MarcoBuster/OrarioTreniBot"}],'\ + '[{"text": "🚦 Treni in tracciamento", "callback_data": "lista_tracciamento"}],' '[{"text":"🔙Torna indietro","callback_data":"home"}]]}'}) + if callback_q == "lista_tracciamento": + c.execute('SELECT * FROM tracciamento WHERE userid=?', (chat.id,)) + rows = c.fetchall() + + text = "Ecco la lista dei treni che sono in tracciamento in questa chat" + + if not rows: + text = text + "\n\nNon stai tracciando nessun treno, per tracciarne uno, cerca un treno e premi su 🚦 Traccia treno" + + for res in rows: + request_id = str(res[0]) + user_id = res[1] + id_treno = res[2] + solo_oggi = res[3] + stazione_ultimo_rilevamento = res[4] + random_string = res[5] + + if solo_oggi == True: + solo_oggi = "stai tracciando questo treno solo per oggi" + else: + solo_oggi = "il tracciamento del treno continuerà fino a quando non sarà annullato" + + text = text + "\n➡️ 🚅Treno {treno}, {solo_oggi}, #tr{random}".format(treno=id_treno, solo_oggi=solo_oggi, id=request_id, random=random_string) + + bot.api.call("editMessageText", + {"chat_id":chat.id, "message_id": message.message_id, "text": text, "parse_mode": "HTML", "reply_markup": + '{"inline_keyboard":[[{"text":"🔙Torna indietro","callback_data":"home"}]]}'}) + + if callback_q == "stats": data, success, error = API.orarioTreni.cercaStatistiche() messaggio = API.Messaggi.statistiche(data) diff --git a/Callback/Tracciamento.py b/Callback/Tracciamento.py new file mode 100644 index 0000000..b9b617f --- /dev/null +++ b/Callback/Tracciamento.py @@ -0,0 +1,47 @@ +import API +import Callback +import Bot + +import sqlite3 +conn = sqlite3.connect('OrarioTreni.db') +c = conn.cursor() + +def callback(bot, chains, update): + API.db.creaTutto() + message = update.callback_query.message + chat = message.chat + query = str(update.callback_query.data) + callback_id = update.callback_query.id + + if "stop_tracciamento" in query: + query = query.split("T") + request_id = query[1] + + c.execute('SELECT * FROM tracciamento WHERE request_id=?', (request_id,)) + rows = c.fetchall() + + for res in rows: + id_treno = str(res[2]) + + c.execute('DELETE FROM tracciamento WHERE request_id=?', (request_id,)) + conn.commit() + + text = ( + "🚦 Traccia treno [BETA]" + "\nTracciamento del treno {treno} interrotto".format(treno=id_treno) + ) + + bot.api.call("editMessageText", { + "chat_id": chat.id, "message_id": message.message_id, "text": text, "parse_mode": "HTML" + }) + + text = ( + "🚦 Traccia treno [BETA]" + "\nTracciamento del treno {treno} interrotto" + "\nPuoi sempre annullare questa operazione".format(treno=id_treno) + ) + + bot.api.call("sendMessage", { + "chat_id": chat.id, "text": text, "parse_mode": "HTML", "reply_markup": + '{"inline_keyboard": [[{"text": "🚅 Traccia il treno '+id_treno+'", "callback_data": "traccia@'+id_treno+'"}]]}' + }) diff --git a/Callback/Treni.py b/Callback/Treni.py index 99ecab9..9efa283 100644 --- a/Callback/Treni.py +++ b/Callback/Treni.py @@ -7,6 +7,8 @@ conn = sqlite3.connect('OrarioTreni.db') c = conn.cursor() +import os + def callback(bot, chains, update): API.db.creaTutto() message = update.callback_query.message @@ -37,6 +39,8 @@ def callback(bot, chains, update): "reply_markup": '{"inline_keyboard":[[{"text":"🚉Lista fermate","callback_data":"'+callbackdata1+'"},' '{"text":"🔄Aggiorna le informazioni","callback_data":"'+callbackdata2+'"}],' + '[{"text": "🚦Traccia il treno [BETA]", "callback_data": "traccia@'+id_treno+'"},' + '{"text": "📊Grafico ritardo", "callback_data": "grafico@'+id_treno+'"}],' '[{"text":"🔙Torna indietro","callback_data":"home"}]]}'}) except botogram.api.APIError: bot.api.call("answerCallbackQuery", {"callback_query_id": callback_id, @@ -114,3 +118,65 @@ def callback(bot, chains, update): '[{"text":"🔙Torna indietro", "callback_data":"list@'+id_treno+'"}]' ']}' }) + + if azione == "traccia": + if "T" in id_treno: + stringa = id_treno.split("T") + id_treno = stringa[0] + azione_2 = stringa[1] + + if azione_2 == "oggi": + solo_oggi = True + stringa_solo_oggi = "Il treno verrà tracciato solo per oggi" + if azione_2 == "sempre": + solo_oggi = False + stringa_solo_oggi = "Il treno verrà tracciato fino a interruzione" + + result = API.db.tracciaTreno(message.chat.id, id_treno, solo_oggi) + if type(result) == str: + bot.api.call("answerCallbackQuery", { + "callback_query_id": callback_id, "text": result, "show_alert": True + }) + return + + text = ( + "🚦 Traccia treno" + "\n🚅 Sto tracciando il treno {treno}" + "\n\n{solo_oggi}".format(solo_oggi=stringa_solo_oggi, treno=id_treno) + ) + bot.api.call("editMessageText", { + "chat_id": chat.id, "message_id": message.message_id, "text": text, "parse_mode": "HTML", "reply_markup": + '{"inline_keyboard": [[{"text": "❌ Annulla il tracciamento", "callback_data": "stop_tracciamentoT'+str(result)+'"}]]}' + }) + + return + + text = ( + "Traccia treno {treno}" + "\nVuoi tracciare questo treno solo oggi o ricevere notifiche tutti i giorni?".format(treno=id_treno) + ) + bot.api.call("editMessageText", { + "chat_id": chat.id, "message_id": message.message_id, "text": text, "parse_mode": "HTML", "reply_markup": + '{"inline_keyboard":' + '[[{"text": "📅 Traccia solo per oggi", "callback_data": "traccia@'+id_treno+'Toggi"}, {"text": "🗓 Traccia tutti i giorni", "callback_data": "traccia@'+id_treno+'Tsempre"}],' + '[{"text":"🔙Torna indietro", "callback_data": "agg@'+id_treno+'"}]]}' + }) + + if azione == "grafico": + data, success, error = API.orarioTreni.cercaTreno(id_treno) + bot.api.call("answerCallbackQuery", { + "callback_query_id": callback_id, "text": "📊Sto generando il grafico, attendere..." + }) + + filename = API.Messaggi.grafico(data, id_treno) + + if filename == False: + text = ( + "❌Impossibile generare il grafico: troppi pochi dati" + "\nAttendi che il treno fermi in qualche altra fermata e ritenta!" + ) + message.reply(text) + return + + message.reply_with_photo(filename, caption="Grafico del treno "+id_treno+" generato con @OrarioTreniBot") + os.remove(filename) diff --git a/Inlinemode/Inline.py b/Inlinemode/Inline.py index 435ee92..59fefdf 100644 --- a/Inlinemode/Inline.py +++ b/Inlinemode/Inline.py @@ -12,6 +12,8 @@ def process(bot, chains, update): sender = update.inline_query.sender query = update.inline_query.query + Bot.logger.info("Utente {} inline query {}".format(update.inline_query.sender.id, update.inline_query.query)) + if not query: testo = "Avviami in privata per scoprire come si usa questo bot in modalità inline" bot.api.call("answerInlineQuery", { diff --git a/Tracciamento.py b/Tracciamento.py new file mode 100644 index 0000000..6ab0725 --- /dev/null +++ b/Tracciamento.py @@ -0,0 +1,128 @@ +import API +import Bot +from CONFIG import TOKEN +from Callback import * +from Inlinemode import * + +import time + +import sqlite3 +conn = sqlite3.connect('OrarioTreni.db') +c = conn.cursor() +API.db.creaTutto() + +import botogram +bot = botogram.create(TOKEN) + +import logging +logger = logging.getLogger("tracciamento") + +format = "%(asctime)s [%(levelname)s]: %(message)s" +level = logging.DEBUG +logging.basicConfig(format=format, level=level) + +def tracciamento(): + c.execute('''SELECT * FROM tracciamento''') + rows = c.fetchall() + if not rows: + logging.debug("Nessun treno da tracciare") + return + + for res in rows: + request_id = str(res[0]) + user_id = res[1] + id_treno = res[2] + solo_oggi = res[3] + stazione_ultimo_rilevamento = res[4] + random_string = res[5] + + data, success, error = API.orarioTreni.cercaTreno(str(id_treno)) + + if data == None: + continue + + stazione_attuale = data['stazioneUltimoRilevamento'] + if stazione_attuale == "--": + stazione_attuale = data['origine'] + + logging.info("Processando la richiesta numero {} del treno {} da {}".format(request_id, id_treno, user_id)) + + if stazione_ultimo_rilevamento == data['destinazione'] and stazione_ultimo_rilevamento == data['stazioneUltimoRilevamento']: + continue + + if stazione_ultimo_rilevamento == data['destinazione'] and stazione_attuale == data['origine']: + c.execute('''UPDATE tracciamento SET stazione_ultimo_rilevamento=? WHERE request_id=?''', (stazione_attuale, request_id,)) + conn.commit() + continue + + if stazione_attuale != stazione_ultimo_rilevamento: + logging.info("Richiesta numero {}, il treno ha cambiato stazione da {} a {}".format(id_treno, stazione_ultimo_rilevamento, stazione_attuale)) + c.execute('''UPDATE tracciamento SET stazione_ultimo_rilevamento=? WHERE request_id=?''', (stazione_attuale, request_id,)) + text = ( + "🚦Traccia treno [BETA]" + "\n🚅Il treno {treno} ha cambiato stazione!" + "\n🚉{precedente} ➡️ 🚉{successiva}" + "\n🕒Ritardo: {ritardo}m".format(treno=id_treno, precedente=stazione_ultimo_rilevamento, successiva=stazione_attuale, ritardo=data['ritardo']) + ) + + if stazione_attuale in str(data['fermate']): + numero_fermata = -1 + for dict in data['fermate']: + numero_fermata = numero_fermata + 1 + if stazione_attuale == dict['stazione']: + break + + try: + fermata = API.Messaggi.fermata(data, numero_fermata) + text = text + "\n\n" + fermata + except Exception as e: + logger.error("Errore nella formattazione della fermata: {}".format(e)) + text = text + "\n\n" + "Errore sconosciuto. Contattare lo sviluppatore. {}".format(e) + + text = text + "\n➡️Clicca qui per seguire tutto il tracciamento: #tracciamento{N}".format(N=random_string) + + else: + text = text + "\n\n" + "Il treno non ferma in questa fermata" + text = text + "\n\n➡️Clicca qui per seguire tutto il tracciamento: #tracciamento{N}".format(N=random_string) + + bot.api.call("sendMessage", { + "chat_id": user_id, "text": text, "parse_mode": "HTML", "reply_markup": + '{"inline_keyboard": [[{"text": "❌ Disattiva le notifiche", "callback_data": "stop_tracciamentoT'+request_id+'"}]]}' + }) + + if stazione_attuale == data['destinazione']: + if solo_oggi == True: + c.execute('DELETE FROM tracciamento WHERE request_id=?', (request_id,)) + logging.info("Utente {} tracciamento cancellato {}".format(user_id, request_id)) + conn.commit() + + text = ( + "🚦Traccia treno [BETA]" + "\nIl treno {treno} è arrivato a destinazione con un ritardo di {ritardo} minuti!" + "\nHo interrotto il tracciamento".format(treno=id_treno, ritardo=data['ritardo']) + ) + bot.api.call("sendMessage", { + "chat_id": user_id, "text": text, "parse_mode": "HTML" + }) + + if solo_oggi == False: + c.execute('''UPDATE tracciamento SET stazione_ultimo_rilevamento=? WHERE request_id=?''', (data['destinazione'], request_id,)) + text = ( + "🚦Traccia treno [BETA]" + "\nIl treno {treno} è arrivato a destinazione con un ritardo di {ritardo} minuti" + "\nDomani riceverai ancora notifiche sul treno".format(treno=id_treno, ritardo=data['ritardo']) + ) + bot.api.call("sendMessage", { + "chat_id": user_id, "text": text, "parse_mode": "HTML", "reply_markup": + '{"inline_keyboard": [[{"text": "❌ Disattiva le notifiche", "callback_data": "stop_tracciamentoT'+request_id+'"}]]}' + }) + + + conn.commit() + +while True: + try: + tracciamento() + except Exception as e: + logging.exception(e) + time.sleep(5) From c334fbdb673e62688a085c3edf1ffe783326ea7d Mon Sep 17 00:00:00 2001 From: Marco Date: Sun, 1 Jan 2017 16:54:27 +0100 Subject: [PATCH 2/2] Making GitHub happy --- Bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bot.py b/Bot.py index 2f701e4..0533a96 100644 --- a/Bot.py +++ b/Bot.py @@ -150,7 +150,7 @@ def post(chat, message, args): bot.chat(res[0]).send(message) chat.send("Post sent to "+str(res[0])) except botogram.api.ChatUnavailableError: - c.execute('DELETE FROM stato WHERE userid=?', (user_id,)) + c.execute('DELETE FROM stato WHERE userid = (?)', (res[0],)) chat.send("The user "+str(res[0])+" has blocked your bot, so I removed him from the database") conn.commit() except Exception as e: