Telegrama como um data warehouse para projetos de TI

Boa tarde, hoje eu gostaria de compartilhar com você os problemas e suas soluções incomuns que foram encontradas ao escrever pequenos projetos de TI. Devo dizer imediatamente que o artigo é para aqueles que são um pouco versados ​​no desenvolvimento de telegramas para bots, bancos de dados, SQL e a linguagem de programação python.

Todo o projeto é postado no github, o link estará no final do artigo.

imagem

Problema principal


Inicialmente, eu queria escrever para mim um contador simples de calorias de telegrama para bot que recebe um número do usuário e retorna quantas calorias são deixadas para o normal durante o dia. Ou seja, você precisa armazenar aproximadamente duas variáveis ​​para cada usuário.

No final, você teve que escolher uma maneira de armazenar esses dados.

  1. Opção - variáveis ​​globais , memória de acesso aleatório. A opção é imediatamente uma falha, porque quando o programa falha, perdemos tudo
  2. Opção - grave em um arquivo no disco. Pode funcionar para esse projeto, mas planejei implantar um bot no heroku, que apaga todos os dados do disco todos os dias. Portanto, essa opção não se encaixava
  3. Opção - planilhas do Google . Inicialmente, queria me debruçar sobre essa opção, mas comecei a entender e perceber que havia um limite no número de consultas na tabela e, para começar a usar a tabela, você precisa escrever várias linhas de código e descobrir sua API não tão simples
  4. Option é um banco de dados . Sim, esta é a melhor opção em tudo. Mas, para esse projeto, é até engraçado de usar. Além disso, a implantação e o suporte de um banco de dados em um servidor de terceiros custará um centavo.

Como resultado, nenhuma dessas opções surgiu. Claro, existem dezenas de outras maneiras, mas gostaria que fosse gratuito, rápido e com um mínimo de código.

Solução


A idéia é muito simples: para armazenamento de dados, usaremos o banco de dados sqllite na memória, pois ele já está incorporado no python 3 e faremos backup de nossa tabela nos servidores Telegram com um pequeno intervalo (aproximadamente a cada 30 segundos) e backup quando o processo do programa estiver fechado.

Se o servidor travar, na primeira solicitação, baixaremos automaticamente nossa tabela do servidor Telegram e restauraremos os dados para sqllite.

Você pode usar qualquer outro no banco de dados de memória que desejar.

Prós


  1. Desempenho - devido ao trabalho com dados na memória principal, a velocidade de execução do programa é ainda mais rápida do que quando o banco de dados é usado em um servidor de terceiros (os gráficos de velocidade e teste estarão no final)
  2. Grátis - não é necessário comprar servidores de terceiros para bancos de dados e todos os dados são armazenados como backup gratuitamente nos servidores de telegrama
  3. Relativamente confiável - se o servidor travar por razões desconhecidas, perderemos o máximo de dados nos últimos 30 segundos (intervalo de backup), para um protótipo em funcionamento ou um projeto pequeno, será suficiente.
  4. Custos mínimos ao alternar para um banco de dados regular - é necessário substituir os dados da conexão, remover o código de backup e transferir os dados da tabela do backup para um novo banco de dados.

Contras


  1. Falta de escala horizontal
  2. Você precisa de duas contas Telegram (uma para o administrador e outra para teste do usuário)
  3. O servidor não funcionará na Rússia devido a bloqueios
  4. Nos comentários, acho que você encontrará uma dúzia de outras nuances.

Hora de cagar


Vamos escrever um clicker simples e executar testes de velocidade.

O bot será escrito na linguagem de programação python usando uma biblioteca de interação assíncrona com o aiograma do telegrama da api.

A primeira coisa a fazer é preencher as configurações do bot, não vou contar como obter um token no BotFather, já existem centenas de artigos sobre esse tópico.

Também precisamos de uma segunda conta em um telegrama para o administrador, na qual nossos backups serão salvos.

Para obter admin_id e config_id, precisamos iniciar o bot a partir da conta de administrador e escrever "admin" no bot, após o qual ele criará o primeiro backup e gravará seu admin_id, config_id. Substitua e inicie o bot novamente.

#-------------------- ------------------------- #    BotFather TOKEN = '1234567:your_token' #  logging.basicConfig(level=logging.INFO) bot = Bot(token=TOKEN) dp = Dispatcher(bot) #             admin_id=12345678 config_id=12345 conn = sqlite3.connect(":memory:") #  in memory  cursor = conn.cursor() 

Então agora vamos analisar a lógica principal do bot


Se o bot receber uma mensagem com a palavra "admin", criamos uma tabela de usuários com o seguinte modelo de dados:

  • chatid - usuário exclusivo de ID de bate-papo
  • nome - nome de usuário
  • click - número de cliques
  • state - o valor da máquina de estado, não é usado neste projeto, mas você não pode prescindir das mais complexas

Adicione um usuário de teste e envie o documento para o servidor Telegram com nossa tabela. Também enviamos admin_id e config_id ao administrador na forma de mensagens. Depois de receber os IDs, esse código precisa ser comentado.

 #    if message.text == 'admin': cursor.execute("CREATE TABLE users (chatid INTEGER , name TEXT, click INTEGER, state INTEGER)") cursor.execute("INSERT INTO users VALUES (1234, 'eee', 1,0)") conn.commit() sql = "SELECT * FROM users " cursor.execute(sql) data = cursor.fetchall() str_data = json.dumps(data) await bot.send_document(message.chat.id, io.StringIO(str_data)) await bot.send_message(message.chat.id, 'admin_id = {}'.format(message.chat.id)) await bot.send_message(message.chat.id, 'config_id = {}'.format(message.message_id+1)) 

Lógica do Usuário


Primeiro, tentamos obter do banco de dados na memória os dados do usuário que enviou a mensagem. Se detectarmos um erro, carregue os dados do backup do servidor Telergam, preencha nosso banco de dados com dados do backup e tente novamente para encontrar o usuário.

 #    try: sql = "SELECT * FROM users where chatid={}".format(message.chat.id) cursor.execute(sql) data = cursor.fetchone() # or use fetchone() except Exception: data = await get_data() cursor.execute("CREATE TABLE users (chatid INTEGER , name TEXT, click INTEGER, state INTEGER)") cursor.executemany("INSERT INTO users VALUES (?,?,?,?)", data) conn.commit() sql = "SELECT * FROM users where chatid={}".format(message.chat.id) cursor.execute(sql) data = cursor.fetchone() # or use fetchone() 

Se encontramos o usuário no banco de dados, processamos os botões:

  • Ao clicar em "Clique", atualizamos o número de cliques para esse usuário
  • Quando você clica em "Classificação", exibimos uma lista das quinze pessoas com mais cliques.

Se você não encontrou o usuário, escreva um erro para ele.

  #      click     if data is not None: if message.text == '': sql = "UPDATE users SET click = {} WHERE chatid = {}".format(data[2]+1,message.chat.id) cursor.execute(sql) conn.commit() await bot.send_message(message.chat.id, ': {} '.format(data[2]+1)) #        10 if message.text == '': sql = "SELECT * FROM users ORDER BY click DESC LIMIT 15" cursor.execute(sql) newlist = cursor.fetchall() # or use fetchone() sql_count = "SELECT COUNT(chatid) FROM users" cursor.execute(sql_count) count=cursor.fetchone() rating=': {}\n'.format(count[0]) i=1 for user in newlist: rating=rating+str(i)+': '+user[1]+' - '+str(user[2])+'\n' i+=1 await bot.send_message(message.chat.id, rating) else: await bot.send_message(message.chat.id, '  ') 

Vamos escrever a lógica para o registro do usuário


Estamos tentando encontrar o usuário no banco de dados, se ele não estiver lá, adicione uma nova linha à tabela e faça um backup.

Se detectarmos um erro, carregaremos o último backup, preencheremos a tabela e repetiremos a tentativa de registro.

 sql_select = "SELECT * FROM users where chatid={}".format(message.chat.id) sql_insert = "INSERT INTO users VALUES ({}, '{}', {},{})".format(message.chat.id,message.chat.first_name, 0, 0) try: cursor.execute(sql_select) data = cursor.fetchone() if data is None: cursor.execute(sql_insert) conn.commit() await save_data() except Exception: data = await get_data() cursor.execute("CREATE TABLE users (chatid INTEGER , name TEXT, click INTEGER, state INTEGER)") cursor.executemany("INSERT INTO users VALUES (?,?,?,?)", data) conn.commit() cursor.execute(sql_select) data = cursor.fetchone() if data is None: cursor.execute(sql_insert) conn.commit() await save_data() #   button = KeyboardButton('') button2 = KeyboardButton('') #  kb = ReplyKeyboardMarkup(resize_keyboard=True).add(button).add(button2) #     await bot.send_message(message.chat.id,' {}'.format(message.chat.first_name),reply_markup=kb) 

Então, bem, e o mais interessante.

Salvando e recuperando dados do servidor Telergam


Nós descarregamos todos os dados da tabela de usuários, convertemos o dicionário em uma string e modificamos nosso arquivo, que é armazenado nos servidores Telegram.

  #-------------------- ------------------------- async def save_data(): sql = "SELECT * FROM users " cursor.execute(sql) data = cursor.fetchall() # or use fetchone() try: #     str_data=json.dumps(data) #      await bot.edit_message_media(InputMediaDocument(io.StringIO(str_data)), admin_id, config_id) except Exception as ex: print(ex) 

Para obter um backup, precisamos encaminhar a mensagem com o arquivo do administrador para o administrador. Em seguida, obtenha o caminho para o arquivo, leia os dados por url e retorne o backup inteiro.

 # #-------------------- ------------------------- async def get_data(): #         forward_data = await bot.forward_message(admin_id, admin_id, config_id) #    ,   file_data = await bot.get_file(forward_data.document.file_id) #    url file_url_data = bot.get_file_url(file_data.file_path) #     json_file= urlopen(file_url_data).read() #    json     return json.loads(json_file) 

Bem, isso é quase tudo, resta apenas escrever um cronômetro para fazer backups e testar o bot.
Crie um encadeamento que execute nosso método save_data () a cada 30 segundos.

 def timer_start(): threading.Timer(30.0, timer_start).start() try: asyncio.run_coroutine_threadsafe(save_data(),bot.loop) except Exception as exc: pass 

Bem, no programa principal, iniciamos o cronômetro e o bot em si.

 #-------------------- ------------------------- if __name__ == '__main__': timer_start() executor.start_polling(dp, skip_updates=True) 

Portanto, o código parece estar resolvido, eis o link do rascunho de trabalho para o github .

Como executar


  1. Faça o download do projeto no github. Iniciamos o projeto em qualquer ambiente de desenvolvimento para python (por exemplo: PyCharm).
  2. O ambiente de desenvolvimento carregará automaticamente as bibliotecas necessárias do arquivo de requisitos.
  3. Substitua Token do BotFather em main.py

  4. Começamos o projeto
  5. Na segunda conta, clique em / start e escreva a palavra "admin"

  6. Desative o projeto e preencha admin_id e config_id no arquivo main.py
  7. Iniciamos o projeto e, a partir da conta de usuário, pressionamos start

  8. Lucro

Testes e gráficos


Os testes foram conduzidos em servidores heroku com características mínimas de instância. Portanto, podemos assumir que todos os testes foram realizados em condições mais ou menos iguais.

Os gráficos são feitos a partir de amostras de ~ 100 respostas a pedidos. E a média da amostra é apresentada.

O PostgreSQL no Amazon RDS com características mínimas foi usado como banco de dados em um servidor de terceiros.



Com um milhão de usuários, o tempo de backup se torna um problema.



O tamanho do backup depende inteiramente do seu modelo de dados. No meu caso, com um milhão de usuários, foi obtido um arquivo de dados com 21 megabytes.



Conclusão


Esse método de armazenamento de dados faz sentido para projetos de até um milhão de usuários. Ou seja, para um protótipo ou uma inicialização pessoal, esse método tem direito à vida.

Como resultado, obtivemos um clicker totalmente independente, independente dos bancos de dados remotos.

Aqui está o projeto descrito acima implantado no heroku: @Clicker_fast_bot

Também implementei um projeto mais complexo com essa ideologia: @Random_friend_bot

A semelhança de uma sala de bate-papo e bate-papo para duas pessoas, mas apenas em um telegrama.

Ele está procurando dentro de um raio de 100 km uma pessoa do sexo oposto em busca de comunicação e realiza uma conversa fechada com um novo interlocutor.

Se for interessante, posso descartar o código fonte do projeto. Além disso, se esse tópico for relevante, no próximo artigo eu posso descrever a criação da API Rest sem bancos de dados externos. Essa é uma pilha de django-sqllite-Telegram.

Ficarei feliz em qualquer crítica, obrigado por sua atenção!

Source: https://habr.com/ru/post/pt464865/


All Articles