-
Notifications
You must be signed in to change notification settings - Fork 20
Красилов Константин - 3 #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 14 commits
acf18b8
9f4d54f
1e1bc2a
2c00fc6
7c1b9ae
daf5946
12c8eca
8736004
a1b7a1c
489069b
8163986
fb81bb9
d81b5d8
56e0308
c9bba57
79d23cb
08da707
2714874
bb18cb6
0f131e8
7c3b816
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| source 'https://rubygems.org' | ||
|
|
||
| gem 'telegramAPI' | ||
| gem 'sinatra' | ||
|
|
||
| gem 'sinatra-activerecord' | ||
| gem 'activerecord' | ||
| gem 'pg' | ||
|
|
||
| gem 'dotenv-rails' | ||
| gem 'json' | ||
| gem 'progress_bar' | ||
|
|
||
| gem 'whenever', require: false | ||
| gem 'rake' | ||
|
|
||
| gem 'rspec' | ||
| gem 'factory_bot' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require 'sinatra/activerecord/rake' | ||
| require './app' | ||
|
|
||
| namespace :db do | ||
| task :load_config do | ||
Saicheg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| require './app' | ||
| end | ||
| end | ||
|
|
||
| Dir.glob('lib/tasks/*.rake').each { |r| load r } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # Telegram-Bot "English lessons" | ||
|
|
||
| ## Описание | ||
|
|
||
| Приложение, которое помогает учить английский слова! | ||
| После регистрации каждому пользователю с некоторой периодичностью отправляются новые английские слова для обучения. | ||
| Пользователь может выбрать сколько слов в день он хочет учить! | ||
|
|
||
| ## Пример взаимодействия | ||
|
|
||
| > /start | ||
| > | ||
| > Привет! Я бот, который помогает учить новые английские слова каждый день. Давай сперва определимся,' \ | ||
| 'сколько слов в день (от 1 до 6 ) ты хочешь узнавать? | ||
| > | ||
| > 7 | ||
| > | ||
| > Сорри, только 6 слов. Давай еще раз? | ||
| > | ||
| > 4 | ||
| > | ||
| > Принято! | ||
|
|
||
| Через какое-то время от бота должно прийти сообщение похожего смысла: | ||
|
|
||
| > **Opinion** - 1 unproven belief. 2 view held as probable. 3 what one thinks about something. 4 piece of professional advice (a second opinion). 5 estimation (low opinion of). [latin: related to *opine] | ||
|
|
||
| Если человек не отреагировал на слово, то бот напомнит ему об этом: | ||
|
|
||
| > Кажется, ты был слишком занят и пропустил слово выше? Дай мне знать, что у тебя все хорошо! | ||
| > | ||
| > 😘 | ||
| > | ||
| > Вижу, что ты заметил слово! Продолжаем учиться дальше! | ||
|
|
||
| ## Установка | ||
|
|
||
| Для корректной работы программы на вашем компьютере должен быть установлены [Ruby](https://www.ruby-lang.org/en/), | ||
| база данных [Postgres](https://www.postgresql.org/) и [Ngrok](https://ngrok.com/). | ||
| Запустите сервер `ngrok`: | ||
| ``` | ||
| $ ./ngrok http 4567 | ||
| ``` | ||
| Создайте своего [telegram бота](https://core.telegram.org/bots). | ||
| Пропишите переменные окружения в .env файле: | ||
| ``` | ||
| TELEGRAM_TOKEN=Ваш токен telegram | ||
| DATABASE_USER=Ваши_данные | ||
| DATABASE_PASSWORD=Ваши_данные | ||
| DATABASE_NAME=Ваши_данные | ||
| URL=https url который выдал ngrok | ||
| ``` | ||
| Запустите комманды: | ||
| ``` | ||
| $ bundle | ||
| $ bunlde exec rake db:migrate db:seed | ||
| $ whenever --update-crontab | ||
| ``` | ||
| ## Запуск | ||
| Запуск осуществляется командой: | ||
| ``` | ||
| $ bundle exec ruby app.rb | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require 'telegramAPI' | ||
| require 'sinatra' | ||
| require 'dotenv' | ||
| require 'sinatra/activerecord' | ||
| require_relative 'config/connection' | ||
| require_relative 'app/models/user' | ||
| require_relative 'app/services/telegram/conversation' | ||
|
|
||
| Dotenv.load | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Лайк за использование ENV переменных 👍 |
||
|
|
||
| api = TelegramAPI.new ENV['TELEGRAM_TOKEN'] | ||
|
|
||
| post '/telegram' do | ||
| status 200 | ||
|
|
||
| request.body.rewind | ||
| data = JSON.parse(request.body.read) | ||
|
|
||
| chat_id = data['message']['chat']['id'] | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Чисто в теории у тебя тут очень легко может случиться ошибка, которую ты не обработаешь. Например Чтобы от такого застраховаться - пойди узнать про метод
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Как лучше обрабатывать ошибки? Просто добавить или обернуть все в чтобы программа продолжала работать. И с ошибками или просто добавить метод |
||
| message = data['message']['text'] | ||
|
|
||
| user = User.find_or_create_by(telegram_id: chat_id) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| case message | ||
| when '/start' | ||
| user.send_max_word! | ||
|
||
| api.sendMessage(chat_id, Telegram::Conversation::RESPONSE[:welcome]) | ||
| else | ||
| api.sendMessage(chat_id, Telegram::Conversation.new(user, message).call) unless user.conversation_break? | ||
| end | ||
|
|
||
| # Return an empty json, to say "ok" to Telegram | ||
| '{}' | ||
| end | ||
|
|
||
| api.setWebhook(ENV['URL']) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class User < ActiveRecord::Base | ||
Saicheg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| enum conversation_status: { | ||
| send_max_word: 0, | ||
| send_smiley: 1, | ||
| conversation_break: 2 | ||
| } | ||
|
|
||
| has_many :user_words, dependent: :destroy | ||
| has_many :words, through: :user_words | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class UserWord < ActiveRecord::Base | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rails/ApplicationRecord: Models should subclass ApplicationRecord. |
||
| belongs_to :user | ||
| belongs_to :word | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class Word < ActiveRecord::Base | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rails/ApplicationRecord: Models should subclass ApplicationRecord. |
||
| has_many :user_words, dependent: :destroy | ||
| has_many :users, through: :user_words | ||
|
|
||
| def to_s | ||
| "#{value} - #{meaning}" | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Telegram | ||
| # Класс который общаеться с пользователем - реагирует на ответы в зависимости от статуса беседы. | ||
| class Conversation | ||
| RESPONSE = { | ||
| welcome: 'Привет! Я бот, который помогает учить новые английские слова каждый день. Давай сперва определимся,' \ | ||
| 'сколько слов в день (от 1 до 6 ) ты хочешь узнавать?', | ||
| max_word_success: 'Принято!', | ||
| max_word_error: 'Сорри, только 6 слов. Давай еще раз?', | ||
| smiley_success: 'Вижу, что ты заметил слово! Продолжаем учиться дальше!', | ||
| smiley_error: 'Отправь смайл 😉' | ||
| }.freeze | ||
|
|
||
| def initialize(user, message) | ||
| @user = user | ||
| @message = message | ||
| end | ||
|
|
||
| def call | ||
| send(@user.conversation_status) | ||
|
||
| end | ||
|
|
||
| private | ||
|
|
||
| def send_max_word | ||
| return RESPONSE[:max_word_error] unless (1..6).include?(@message.to_i) | ||
|
|
||
| @user.update(max_words: @message, conversation_status: 'conversation_break') | ||
|
||
| RESPONSE[:max_word_success] | ||
| end | ||
|
|
||
| def send_smiley | ||
| return RESPONSE[:smiley_error] unless @message.unpack('U*').any? { |e| e.between?(0x1F600, 0x1F6FF) } | ||
|
|
||
| @user.conversation_break! | ||
| RESPONSE[:smiley_success] | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require 'telegramAPI' | ||
| require 'dotenv' | ||
| require_relative '../../models/user' | ||
| require_relative '../../models/word' | ||
| require_relative '../../models/user_word' | ||
|
|
||
| Dotenv.load | ||
|
|
||
| module Telegram | ||
| # Класс который учит пользователя новым словам. Ищет подходящее слово, отправляет пользователю и ждет реакции. | ||
| class Lesson | ||
| USER_SLEEP = 'Кажется, ты был слишком занят и пропустил слово выше? Дай мне знать, что у тебя все хорошо!' | ||
|
|
||
| def initialize(user) | ||
| @user = user | ||
| @user.send_smiley! | ||
| @api = TelegramAPI.new ENV['TELEGRAM_TOKEN'] | ||
| end | ||
|
|
||
| def call | ||
| find_and_added_word_to_user | ||
| send_word | ||
| waiting_answer | ||
|
||
| end | ||
|
|
||
| private | ||
|
|
||
| def find_and_added_word_to_user | ||
| @word = Word.where.not(id: @user.words).order('RANDOM()').first | ||
| @user.words << @word | ||
| end | ||
|
|
||
| def send_word | ||
| @api.sendMessage(@user.telegram_id, @word.to_s) | ||
| end | ||
|
|
||
| def waiting_answer | ||
|
||
| loop do | ||
| sleep(600) | ||
| @user.reload | ||
|
|
||
| @user.conversation_break? ? break : @api.sendMessage(@user.telegram_id, USER_SLEEP) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require 'sinatra/activerecord' | ||
| require 'dotenv' | ||
|
|
||
| Dotenv.load | ||
|
|
||
| set :database, { | ||
| adapter: 'postgresql', | ||
| encoding: 'unicode', | ||
| pool: 5, | ||
| username: ENV['DATABASE_USER'], | ||
| password: ENV['DATABASE_PASSWORD'], | ||
| database: ENV['DATABASE_NAME'] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| set :output, 'log/cron.log' | ||
|
|
||
| every '0 10 * * 1-7' do | ||
| rake 'telegram:start_lesson[6]' | ||
| end | ||
|
|
||
| every '0 12 * * 1-7' do | ||
| rake 'telegram:start_lesson[5]' | ||
| end | ||
|
|
||
| every '0 14 * * 1-7' do | ||
| rake 'telegram:start_lesson[4]' | ||
| end | ||
|
|
||
| every '0 16 * * 1-7' do | ||
| rake 'telegram:start_lesson[3]' | ||
| end | ||
|
|
||
| every '0 18 * * 1-7' do | ||
| rake 'telegram:start_lesson[2]' | ||
| end | ||
|
|
||
| every '0 20 * * 1-7' do | ||
| rake 'telegram:start_lesson[1]' | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| class CreateUsers < ActiveRecord::Migration[6.1] | ||
| def up | ||
| create_table :users do |t| | ||
| t.integer :telegram_id, null: false, index: { unique: true } | ||
| t.integer :max_words, null: false, default: 1 | ||
| t.integer :conversation_status, null: false, default: 0 | ||
|
|
||
| t.timestamps | ||
| end | ||
| end | ||
|
|
||
| def down | ||
| drop_table :users | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| class CreateWords < ActiveRecord::Migration[6.1] | ||
| def up | ||
| create_table :words do |t| | ||
| t.string :value | ||
| t.text :meaning | ||
|
|
||
| t.timestamps | ||
| end | ||
| end | ||
|
|
||
| def down | ||
| drop_table :words | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| class CreateUserWords < ActiveRecord::Migration[6.1] | ||
| def up | ||
| create_table :user_words do |t| | ||
| t.references :user, null: false, foreign_key: true | ||
| t.references :word, null: false, foreign_key: true | ||
|
|
||
| t.timestamps | ||
| end | ||
| end | ||
|
|
||
| def down | ||
| drop_table :user_words | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require_relative '../app/models/word' | ||
| require 'progress_bar' | ||
| require 'json' | ||
|
|
||
| words = JSON.parse(File.read(File.join(__dir__, 'words.json'))) | ||
| bar = ProgressBar.new(words.count, :bar, :percentage, :elapsed) | ||
|
|
||
| words.each do |word| | ||
| Word.create(value: word[0], meaning: word[1]) | ||
| bar.increment! | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не очень понятно, если честно, что ты хотел тут сказать. Выглядит как ненужный код.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я его добавил чтобы была возможность работать с таксками гема
sinatra/activerecord/rake-rake db:migrate,rake db:seed.https://github.com/sinatra-activerecord/sinatra-activerecord#setup