From 4206f6959960699b52d2639f95416875a6734934 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 6 Mar 2022 20:23:22 +0300 Subject: [PATCH] Version 3 (#44) --- .dockerignore | 1 + .editorconfig | 17 + .env.test | 29 + .gemrc | 1 + .github/workflows/shizoid.yml | 79 +++ .gitignore | 19 +- .localer.yml | 8 - .rspec | 3 + .rubocop.yml | 35 +- .rubocop_todo.yml | 394 +++++++++++++ .ruby-version | 2 +- Capfile | 10 - Dockerfile | 22 + Gemfile | 62 +- Gemfile.lock | 528 +++++++++++------- Guardfile | 24 + README.md | 11 +- Rakefile | 3 +- app/controllers/application_controller.rb | 2 - app/controllers/concerns/bayanizator.rb | 25 - app/controllers/concerns/cool_story.rb | 11 - app/controllers/concerns/databanks.rb | 30 - app/controllers/concerns/eightball.rb | 23 - app/controllers/concerns/gab.rb | 16 - app/controllers/concerns/greeting.rb | 18 - app/controllers/concerns/help.rb | 5 - app/controllers/concerns/helper_methods.rb | 77 --- app/controllers/concerns/ids.rb | 7 - app/controllers/concerns/locale.rb | 19 - app/controllers/concerns/me.rb | 9 - app/controllers/concerns/ping.rb | 5 - app/controllers/concerns/say.rb | 7 - app/controllers/concerns/status.rb | 15 - app/controllers/concerns/winner_of_the_day.rb | 52 -- app/controllers/shizoid_controller.rb | 123 ---- app/controllers/webhooks_controller.rb | 28 + app/jobs/application_job.rb | 2 - app/models/application_record.rb | 2 + app/models/chat.rb | 123 ++-- app/models/covid_stat.rb | 88 +++ app/models/data_bank.rb | 2 + app/models/greeting.rb | 15 - app/models/pair.rb | 63 ++- app/models/participation.rb | 32 +- app/models/reply.rb | 4 +- app/models/single.rb | 48 -- app/models/url.rb | 7 - app/models/user.rb | 20 + app/models/winner.rb | 65 +-- app/models/word.rb | 17 +- app/processors/concerns/command_parameters.rb | 17 + app/processors/concerns/processor.rb | 74 +++ app/processors/message_processor.rb | 32 ++ .../message_processor/binary_dice.rb | 23 + .../message_processor/cool_story.rb | 27 + .../message_processor/covid_stats.rb | 60 ++ app/processors/message_processor/databank.rb | 60 ++ app/processors/message_processor/eightball.rb | 50 ++ app/processors/message_processor/gab.rb | 40 ++ app/processors/message_processor/help.rb | 18 + app/processors/message_processor/holiday.rb | 18 + app/processors/message_processor/ids.rb | 26 + app/processors/message_processor/me.rb | 29 + app/processors/message_processor/ping.rb | 17 + app/processors/message_processor/say.rb | 22 + app/processors/message_processor/start.rb | 16 + app/processors/message_processor/status.rb | 31 + app/processors/message_processor/stop.rb | 17 + app/processors/message_processor/text.rb | 46 ++ app/processors/message_processor/winner.rb | 84 +++ app/processors/update_processor.rb | 17 + app/services/redis_service.rb | 11 + app/services/stop_covid_service.rb | 22 + app/services/telegram_service.rb | 46 ++ app/workers/chat_destroyer.rb | 9 - app/workers/chat_updater.rb | 15 - app/workers/context_updater.rb | 9 - app/workers/covid_region_updater_worker.rb | 12 + app/workers/covid_sender_worker.rb | 21 + app/workers/covid_updater_worker.rb | 12 + app/workers/idle_check_worker.rb | 15 + app/workers/message_worker.rb | 10 + app/workers/pair_update_worker.rb | 12 + app/workers/pair_updater.rb | 9 - app/workers/participant_updater.rb | 13 - app/workers/send_payload_worker.rb | 10 + app/workers/stats_updater.rb | 9 - app/workers/update_worker.rb | 10 + app/workers/winner_check_worker.rb | 9 + app/workers/winner_gamble_worker.rb | 27 + bin/bundle | 108 +++- bin/copy_samples | 21 - bin/rails | 11 +- bin/rake | 9 +- bin/rspec | 8 - bin/setup | 28 +- bin/spring | 17 - bin/update | 29 - compose.sh | 3 + config.ru | 5 +- config/application.rb | 42 +- config/boot.rb | 5 +- config/database.yml | 20 + config/database.yml.example | 15 - config/deploy.rb.example | 26 - config/deploy/Containerfile | 11 + config/deploy/Containerfile.receiver | 43 ++ config/deploy/Containerfile.tor | 7 + config/deploy/nginx.conf.erb | 45 -- config/deploy/production.rb | 2 - config/deploy/shizoid.service.erb | 20 - config/deploy/shizoid_sidekiq.service.erb | 20 - config/environment.rb | 2 +- config/environments/development.rb | 37 +- config/environments/production.rb | 41 +- config/environments/test.rb | 40 +- .../application_controller_renderer.rb | 11 +- config/initializers/backtrace_silencers.rb | 9 +- config/initializers/cors.rb | 4 +- .../extensions/telegram/bot/client.rb | 23 + .../initializers/filter_parameter_logging.rb | 4 +- config/initializers/inflections.rb | 8 +- config/initializers/locale.rb | 6 + config/initializers/mime_types.rb | 1 + .../new_framework_defaults_6_1.rb | 64 +++ .../new_framework_defaults_7_0.rb | 117 ++++ config/initializers/redis.rb | 7 - config/initializers/secrets.rb | 5 + config/initializers/sentry.rb | 8 + config/initializers/sidekiq.rb | 17 + config/initializers/wrap_parameters.rb | 2 + config/locales/en.yml | 144 +---- config/locales/ru.yml | 162 ------ config/locales/ru_RU/active_record.yml | 269 +++++++++ config/locales/ru_RU/bayan.yml | 11 + config/locales/ru_RU/binary_dice.yml | 15 + config/locales/ru_RU/cool_story.yml | 6 + config/locales/ru_RU/covid_stat.yml | 97 ++++ config/locales/ru_RU/databank.yml | 8 + config/locales/ru_RU/eightball.yml | 29 + config/locales/ru_RU/gab.yml | 4 + config/locales/ru_RU/help.yml | 13 + config/locales/ru_RU/holidays/01.january.yml | 148 +++++ config/locales/ru_RU/holidays/07.july.yml | 43 ++ config/locales/ru_RU/idle.yml | 7 + config/locales/ru_RU/ids.yml | 4 + config/locales/ru_RU/me.yml | 5 + config/locales/ru_RU/nok.yml | 14 + config/locales/ru_RU/ok.yml | 13 + config/locales/ru_RU/ping.yml | 8 + config/locales/ru_RU/status.yml | 11 + config/locales/ru_RU/support.yml | 8 + config/locales/ru_RU/text.yml | 11 + config/locales/ru_RU/winner.yml | 15 + config/newrelic.yml.example | 15 - config/puma.rb | 58 +- config/routes.rb | 5 +- config/secrets.yml | 40 ++ config/secrets.yml.example | 43 -- config/sidekiq.yml | 13 +- config/sidekiq_schedules/covid_stats.yml | 7 + config/sidekiq_schedules/winner_checks.yml | 3 + db/migrate/20170815153040_create_chats.rb | 2 + db/migrate/20170815155629_create_words.rb | 2 + db/migrate/20170815155843_create_pairs.rb | 2 + db/migrate/20170815173153_create_urls.rb | 2 + db/migrate/20170816152384_create_replies.rb | 2 + db/migrate/20170816162384_create_singles.rb | 2 + db/migrate/20170817155650_create_greetings.rb | 2 + db/migrate/20170817161038_create_winners.rb | 2 + .../20170917105603_persist_chat_i_ds.rb | 2 + ...171028053628_remove_one_world_sentences.rb | 2 + .../20171104065321_create_data_banks.rb | 2 + .../20180203063658_add_dates_to_chat.rb | 2 + .../20180211095834_fix_chat_timestamps.rb | 2 + .../20180320152158_add_participations.rb | 5 +- db/migrate/20190817131016_chat_type_change.rb | 24 + db/migrate/20190817170713_add_users.rb | 36 ++ ...122843_add_experience_to_participations.rb | 7 + .../20210925130635_remove_urls_and_singles.rb | 17 + .../20211029064031_create_covid_stats.rb | 19 + ...211102090851_add_score_to_participation.rb | 6 + .../20211102142926_add_date_to_winner.rb | 8 + .../20211102185358_add_region_to_chat.rb | 10 + db/schema.rb | 75 ++- db/seeds.rb | 8 + docker-compose.yaml | 86 +++ lib/logrotate | 11 - lib/shizoid.lua | 88 --- lib/tasks/cron.rake | 23 - lib/tasks/databank.rake | 2 + lib/tasks/database.rake | 66 +++ lib/tasks/deploy.rake | 52 -- lib/tasks/import.rake | 69 --- lib/tasks/start_polling.rake | 6 + receiver/_dev_start.sh | 5 + receiver/go.mod | 11 + receiver/go.sum | 8 + receiver/main.go | 65 +++ spec/controllers/webhooks_controller_spec.rb | 39 ++ spec/factories/chat.rb | 29 + spec/factories/chats.rb | 36 -- spec/factories/covid_stat.rb | 13 + spec/factories/data_bank.rb | 7 + spec/factories/greetings.rb | 6 - spec/factories/pair.rb | 22 + spec/factories/pairs.rb | 7 - spec/factories/participation.rb | 12 + spec/factories/payloads.rb | 46 -- spec/factories/replies.rb | 7 - spec/factories/single.rb | 11 + spec/factories/singles.rb | 9 - spec/factories/telegram/chat.rb | 19 + spec/factories/telegram/chat_permissions.rb | 14 + spec/factories/telegram/chat_photo.rb | 8 + spec/factories/telegram/leftovers.rb | 105 ++++ spec/factories/telegram/message.rb | 195 +++++++ spec/factories/telegram/update.rb | 20 + spec/factories/telegram/user.rb | 16 + spec/factories/url.rb | 7 + spec/factories/urls.rb | 5 - spec/factories/user.rb | 13 + spec/factories/winner.rb | 9 + spec/factories/winners.rb | 18 - spec/factories/word.rb | 7 + spec/factories/words.rb | 5 - spec/locale_spec.rb | 8 - spec/models/chat_spec.rb | 104 +++- spec/models/covid_stat_spec.rb | 136 +++++ spec/models/greeting_spec.rb | 9 - spec/models/pair_spec.rb | 124 +++- spec/models/participation_spec.rb | 36 ++ spec/models/reply_spec.rb | 9 - spec/models/single_spec.rb | 10 - spec/models/url_spec.rb | 23 - spec/models/user_spec.rb | 42 ++ spec/models/winner_spec.rb | 72 +-- spec/models/word_spec.rb | 66 +-- spec/processors/concerns/processor_spec.rb | 83 +++ .../message_processor/binary_dice_spec.rb | 37 ++ .../message_processor/cool_story_spec.rb | 31 + .../message_processor/covid_stats_spec.rb | 195 +++++++ .../message_processor/databank_spec.rb | 184 ++++++ .../message_processor/eightball_spec.rb | 93 +++ spec/processors/message_processor/gab_spec.rb | 93 +++ .../processors/message_processor/help_spec.rb | 57 ++ spec/processors/message_processor/ids_spec.rb | 43 ++ spec/processors/message_processor/me_spec.rb | 92 +++ .../processors/message_processor/ping_spec.rb | 57 ++ spec/processors/message_processor/say_spec.rb | 82 +++ .../message_processor/start_spec.rb | 69 +++ .../message_processor/status_spec.rb | 66 +++ .../processors/message_processor/stop_spec.rb | 69 +++ .../processors/message_processor/text_spec.rb | 54 ++ .../message_processor/winner_spec.rb | 185 ++++++ spec/processors/message_processor_spec.rb | 62 ++ spec/processors/update_processor_spec.rb | 53 ++ spec/rails_helper.rb | 86 ++- spec/requests/shizoid_controller_spec.rb | 20 - spec/services/stop_covid_service_spec.rb | 22 + spec/spec_helper.rb | 95 +--- spec/support/hashify_telegram_object.rb | 7 + spec/support/telegram.rb | 17 + spec/support/web_mocks.rb | 32 ++ spec/support/web_mocks/stop_covid.rb | 29 + spec/support/web_mocks/telegram.rb | 26 + .../covid_region_updater_worker_spec.rb | 18 + spec/workers/covid_sender_worker_spec.rb | 24 + spec/workers/covid_updater_worker_spec.rb | 37 ++ spec/workers/idle_check_worker_spec.rb | 22 + spec/workers/message_worker_spec.rb | 17 + spec/workers/send_payload_worker_spec.rb | 18 + spec/workers/update_worker_spec.rb | 17 + spec/workers/winner_check_worker_spec.rb | 18 + spec/workers/winner_gamble_worker_spec.rb | 20 + start.sh | 10 + 276 files changed, 7028 insertions(+), 2512 deletions(-) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .env.test create mode 100644 .gemrc create mode 100644 .github/workflows/shizoid.yml delete mode 100644 .localer.yml create mode 100644 .rubocop_todo.yml delete mode 100644 Capfile create mode 100644 Dockerfile create mode 100644 Guardfile delete mode 100644 app/controllers/application_controller.rb delete mode 100644 app/controllers/concerns/bayanizator.rb delete mode 100644 app/controllers/concerns/cool_story.rb delete mode 100644 app/controllers/concerns/databanks.rb delete mode 100644 app/controllers/concerns/eightball.rb delete mode 100644 app/controllers/concerns/gab.rb delete mode 100644 app/controllers/concerns/greeting.rb delete mode 100644 app/controllers/concerns/help.rb delete mode 100644 app/controllers/concerns/helper_methods.rb delete mode 100644 app/controllers/concerns/ids.rb delete mode 100644 app/controllers/concerns/locale.rb delete mode 100644 app/controllers/concerns/me.rb delete mode 100644 app/controllers/concerns/ping.rb delete mode 100644 app/controllers/concerns/say.rb delete mode 100644 app/controllers/concerns/status.rb delete mode 100644 app/controllers/concerns/winner_of_the_day.rb delete mode 100644 app/controllers/shizoid_controller.rb create mode 100644 app/controllers/webhooks_controller.rb delete mode 100644 app/jobs/application_job.rb create mode 100644 app/models/covid_stat.rb delete mode 100644 app/models/greeting.rb delete mode 100644 app/models/single.rb delete mode 100644 app/models/url.rb create mode 100644 app/models/user.rb create mode 100644 app/processors/concerns/command_parameters.rb create mode 100644 app/processors/concerns/processor.rb create mode 100644 app/processors/message_processor.rb create mode 100644 app/processors/message_processor/binary_dice.rb create mode 100644 app/processors/message_processor/cool_story.rb create mode 100644 app/processors/message_processor/covid_stats.rb create mode 100644 app/processors/message_processor/databank.rb create mode 100644 app/processors/message_processor/eightball.rb create mode 100644 app/processors/message_processor/gab.rb create mode 100644 app/processors/message_processor/help.rb create mode 100644 app/processors/message_processor/holiday.rb create mode 100644 app/processors/message_processor/ids.rb create mode 100644 app/processors/message_processor/me.rb create mode 100644 app/processors/message_processor/ping.rb create mode 100644 app/processors/message_processor/say.rb create mode 100644 app/processors/message_processor/start.rb create mode 100644 app/processors/message_processor/status.rb create mode 100644 app/processors/message_processor/stop.rb create mode 100644 app/processors/message_processor/text.rb create mode 100644 app/processors/message_processor/winner.rb create mode 100644 app/processors/update_processor.rb create mode 100644 app/services/redis_service.rb create mode 100644 app/services/stop_covid_service.rb create mode 100644 app/services/telegram_service.rb delete mode 100644 app/workers/chat_destroyer.rb delete mode 100644 app/workers/chat_updater.rb delete mode 100644 app/workers/context_updater.rb create mode 100644 app/workers/covid_region_updater_worker.rb create mode 100644 app/workers/covid_sender_worker.rb create mode 100644 app/workers/covid_updater_worker.rb create mode 100644 app/workers/idle_check_worker.rb create mode 100644 app/workers/message_worker.rb create mode 100644 app/workers/pair_update_worker.rb delete mode 100644 app/workers/pair_updater.rb delete mode 100644 app/workers/participant_updater.rb create mode 100644 app/workers/send_payload_worker.rb delete mode 100644 app/workers/stats_updater.rb create mode 100644 app/workers/update_worker.rb create mode 100644 app/workers/winner_check_worker.rb create mode 100644 app/workers/winner_gamble_worker.rb delete mode 100755 bin/copy_samples delete mode 100755 bin/rspec delete mode 100755 bin/spring delete mode 100755 bin/update create mode 100755 compose.sh create mode 100644 config/database.yml delete mode 100644 config/database.yml.example delete mode 100644 config/deploy.rb.example create mode 100644 config/deploy/Containerfile create mode 100644 config/deploy/Containerfile.receiver create mode 100644 config/deploy/Containerfile.tor delete mode 100644 config/deploy/nginx.conf.erb delete mode 100644 config/deploy/production.rb delete mode 100644 config/deploy/shizoid.service.erb delete mode 100644 config/deploy/shizoid_sidekiq.service.erb create mode 100644 config/initializers/extensions/telegram/bot/client.rb create mode 100644 config/initializers/locale.rb create mode 100644 config/initializers/new_framework_defaults_6_1.rb create mode 100644 config/initializers/new_framework_defaults_7_0.rb delete mode 100644 config/initializers/redis.rb create mode 100644 config/initializers/secrets.rb create mode 100644 config/initializers/sentry.rb create mode 100644 config/initializers/sidekiq.rb delete mode 100644 config/locales/ru.yml create mode 100644 config/locales/ru_RU/active_record.yml create mode 100644 config/locales/ru_RU/bayan.yml create mode 100644 config/locales/ru_RU/binary_dice.yml create mode 100644 config/locales/ru_RU/cool_story.yml create mode 100644 config/locales/ru_RU/covid_stat.yml create mode 100644 config/locales/ru_RU/databank.yml create mode 100644 config/locales/ru_RU/eightball.yml create mode 100644 config/locales/ru_RU/gab.yml create mode 100644 config/locales/ru_RU/help.yml create mode 100644 config/locales/ru_RU/holidays/01.january.yml create mode 100644 config/locales/ru_RU/holidays/07.july.yml create mode 100644 config/locales/ru_RU/idle.yml create mode 100644 config/locales/ru_RU/ids.yml create mode 100644 config/locales/ru_RU/me.yml create mode 100644 config/locales/ru_RU/nok.yml create mode 100644 config/locales/ru_RU/ok.yml create mode 100644 config/locales/ru_RU/ping.yml create mode 100644 config/locales/ru_RU/status.yml create mode 100644 config/locales/ru_RU/support.yml create mode 100644 config/locales/ru_RU/text.yml create mode 100644 config/locales/ru_RU/winner.yml delete mode 100644 config/newrelic.yml.example create mode 100644 config/secrets.yml delete mode 100644 config/secrets.yml.example create mode 100644 config/sidekiq_schedules/covid_stats.yml create mode 100644 config/sidekiq_schedules/winner_checks.yml create mode 100644 db/migrate/20190817131016_chat_type_change.rb create mode 100644 db/migrate/20190817170713_add_users.rb create mode 100644 db/migrate/20200209122843_add_experience_to_participations.rb create mode 100644 db/migrate/20210925130635_remove_urls_and_singles.rb create mode 100644 db/migrate/20211029064031_create_covid_stats.rb create mode 100644 db/migrate/20211102090851_add_score_to_participation.rb create mode 100644 db/migrate/20211102142926_add_date_to_winner.rb create mode 100644 db/migrate/20211102185358_add_region_to_chat.rb create mode 100644 db/seeds.rb create mode 100644 docker-compose.yaml delete mode 100644 lib/logrotate delete mode 100644 lib/shizoid.lua delete mode 100644 lib/tasks/cron.rake create mode 100644 lib/tasks/database.rake delete mode 100644 lib/tasks/deploy.rake delete mode 100644 lib/tasks/import.rake create mode 100644 lib/tasks/start_polling.rake create mode 100755 receiver/_dev_start.sh create mode 100644 receiver/go.mod create mode 100644 receiver/go.sum create mode 100644 receiver/main.go create mode 100644 spec/controllers/webhooks_controller_spec.rb create mode 100644 spec/factories/chat.rb delete mode 100644 spec/factories/chats.rb create mode 100644 spec/factories/covid_stat.rb create mode 100644 spec/factories/data_bank.rb delete mode 100644 spec/factories/greetings.rb create mode 100644 spec/factories/pair.rb delete mode 100644 spec/factories/pairs.rb create mode 100644 spec/factories/participation.rb delete mode 100644 spec/factories/payloads.rb delete mode 100644 spec/factories/replies.rb create mode 100644 spec/factories/single.rb delete mode 100644 spec/factories/singles.rb create mode 100644 spec/factories/telegram/chat.rb create mode 100644 spec/factories/telegram/chat_permissions.rb create mode 100644 spec/factories/telegram/chat_photo.rb create mode 100644 spec/factories/telegram/leftovers.rb create mode 100644 spec/factories/telegram/message.rb create mode 100644 spec/factories/telegram/update.rb create mode 100644 spec/factories/telegram/user.rb create mode 100644 spec/factories/url.rb delete mode 100644 spec/factories/urls.rb create mode 100644 spec/factories/user.rb create mode 100644 spec/factories/winner.rb delete mode 100644 spec/factories/winners.rb create mode 100644 spec/factories/word.rb delete mode 100644 spec/factories/words.rb delete mode 100644 spec/locale_spec.rb create mode 100644 spec/models/covid_stat_spec.rb delete mode 100644 spec/models/greeting_spec.rb create mode 100644 spec/models/participation_spec.rb delete mode 100644 spec/models/reply_spec.rb delete mode 100644 spec/models/single_spec.rb delete mode 100644 spec/models/url_spec.rb create mode 100644 spec/models/user_spec.rb create mode 100644 spec/processors/concerns/processor_spec.rb create mode 100644 spec/processors/message_processor/binary_dice_spec.rb create mode 100644 spec/processors/message_processor/cool_story_spec.rb create mode 100644 spec/processors/message_processor/covid_stats_spec.rb create mode 100644 spec/processors/message_processor/databank_spec.rb create mode 100644 spec/processors/message_processor/eightball_spec.rb create mode 100644 spec/processors/message_processor/gab_spec.rb create mode 100644 spec/processors/message_processor/help_spec.rb create mode 100644 spec/processors/message_processor/ids_spec.rb create mode 100644 spec/processors/message_processor/me_spec.rb create mode 100644 spec/processors/message_processor/ping_spec.rb create mode 100644 spec/processors/message_processor/say_spec.rb create mode 100644 spec/processors/message_processor/start_spec.rb create mode 100644 spec/processors/message_processor/status_spec.rb create mode 100644 spec/processors/message_processor/stop_spec.rb create mode 100644 spec/processors/message_processor/text_spec.rb create mode 100644 spec/processors/message_processor/winner_spec.rb create mode 100644 spec/processors/message_processor_spec.rb create mode 100644 spec/processors/update_processor_spec.rb delete mode 100644 spec/requests/shizoid_controller_spec.rb create mode 100644 spec/services/stop_covid_service_spec.rb create mode 100644 spec/support/hashify_telegram_object.rb create mode 100644 spec/support/telegram.rb create mode 100644 spec/support/web_mocks.rb create mode 100644 spec/support/web_mocks/stop_covid.rb create mode 100644 spec/support/web_mocks/telegram.rb create mode 100644 spec/workers/covid_region_updater_worker_spec.rb create mode 100644 spec/workers/covid_sender_worker_spec.rb create mode 100644 spec/workers/covid_updater_worker_spec.rb create mode 100644 spec/workers/idle_check_worker_spec.rb create mode 100644 spec/workers/message_worker_spec.rb create mode 100644 spec/workers/send_payload_worker_spec.rb create mode 100644 spec/workers/update_worker_spec.rb create mode 100644 spec/workers/winner_check_worker_spec.rb create mode 100644 spec/workers/winner_gamble_worker_spec.rb create mode 100755 start.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6b8710a --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.git diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d36a5c6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.go] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = tab +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..8be9948 --- /dev/null +++ b/.env.test @@ -0,0 +1,29 @@ +RAILS_SECRET_KEY_BASE=865a7ffac1ee450bd7cd4162f136de03bacdb73644f463512db8da81e21e24e6885b76c104cbb3d0513112e47dc102ce9909a25c62c2061d27b134e29a61e55d +RAILS_ENV=test + +PUMA_MIN_THREADS=2 +PUMA_MAX_THREADS=5 +WEB_CONCURRENCY=0 + +DATABASE_HOST=postgres +DATABASE_NAME=shizoid +DATABASE_PASSWORD=WoZ7w2e3 +DATABASE_PORT=5432 +DATABASE_USERNAME=shizoid +DB_POOL_SIZE=40 + +SIDEKIQ_CONCURRENCY=25 +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_SIDEKIQ_DB=1 +REDIS_CONTEXT_DB=2 +# REDIS_PASSWORD= +CONTEXT_SIZE=50 + +WEBHOOK_URL=https://localhost +TELEGRAM_TOKEN=111111111:11111111111111111111111111111111111 +BOT_OWNERS=11111,222222 +# ALLOW_TO_ALL=true + +#SENTRY_DSN= +STOPCOVID_URL=https://xn--80aesfpebagmfblc0a.xn--p1ai diff --git a/.gemrc b/.gemrc new file mode 100644 index 0000000..bfe57a1 --- /dev/null +++ b/.gemrc @@ -0,0 +1 @@ +install: --no-document diff --git a/.github/workflows/shizoid.yml b/.github/workflows/shizoid.yml new file mode 100644 index 0000000..de7d5f1 --- /dev/null +++ b/.github/workflows/shizoid.yml @@ -0,0 +1,79 @@ +name: Shizoid +on: + - push + - pull_request + +jobs: + rspec: + name: Perform specs + runs-on: ubuntu-latest + services: + postgres: + image: postgres:alpine + env: + POSTGRES_USER: shizoid + POSTGRES_PASSWORD: WoZ7w2e3 + POSTGRES_DB: shizoid_test + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + redis: + image: redis:alpine + ports: + - 6379:6379 + options: --entrypoint redis-server + steps: + - name: 'Checkout repository' + uses: actions/checkout@v2 + - name: 'Setup Ruby' + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.0 + - name: "Install required system packages" + run: sudo apt-get update -y && sudo apt-get install openssh-client rsync libpq-dev cmake -y + - name: "Bundle install" + run: | + gem install bundler && bundle update --bundler && bundle -v + bundle config path vendor/bundle + bundle install --jobs 4 --retry 3 + - name: "Rails test" + run: | + export $(grep -v '#' .env.test | grep '=' | xargs) + export DATABASE_HOST=127.0.0.1 + export REDIS_HOST=127.0.0.1 + bundle exec rails db:setup --trace + bundle exec rails test + bundle exec rspec + bundle exec rails zeitwerk:check + + build: + name: Build and upload image + runs-on: ubuntu-latest + if: ${{ github.ref == 'refs/heads/master' }} + needs: rspec + steps: + - uses: actions/checkout@v2 + + - name: Save current version to file + run: git rev-parse --short HEAD > .version + + - name: Buildah Action + id: build-image + uses: redhat-actions/buildah-build@v2 + with: + image: shizoid + tags: latest ${{ github.sha }} + containerfiles: ./config/deploy/Containerfile + + - name: Push to docker hub + uses: redhat-actions/push-to-registry@v2 + with: + image: ${{ steps.build-image.outputs.image }} + tags: ${{ steps.build-image.outputs.tags }} + registry: docker.io/${{ secrets.DOCKER_USER }} + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_TOKEN }} diff --git a/.gitignore b/.gitignore index f34d5a5..4d4e0c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,15 @@ -/.bundle -/log/* -/tmp/* !/log/.keep !/tmp/.keep .byebug_history -config/secrets.yml -config/database.yml -config/deploy.rb -config/newrelic.yml +.idea +.vagrant +.version +*.sublime-project +*.sublime-workspace +/.bundle +/log/* +/tmp/* +coverage +db/backups +receiver/tmp +.env diff --git a/.localer.yml b/.localer.yml deleted file mode 100644 index e28a678..0000000 --- a/.localer.yml +++ /dev/null @@ -1,8 +0,0 @@ -# Do not check rails i18n, we use default -Exclude: - - /^\.date\.*/ - - /^\.time\.*/ - - /^\.number\.*/ - - /^\.errors\.*/ - - /^\.activerecord\.*/ - - /^\.helpers\.*/ diff --git a/.rspec b/.rspec index 0f30ade..6aa2bf5 100644 --- a/.rspec +++ b/.rspec @@ -1,2 +1,5 @@ --color +--format d +--require spec_helper --require rails_helper +--order rand diff --git a/.rubocop.yml b/.rubocop.yml index 682703e..4b6f57c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,8 +1,37 @@ +inherit_from: .rubocop_todo.yml + +require: + - rubocop-performance + - rubocop-rspec + - rubocop-rails + AllCops: - TargetRubyVersion: 2.5 + TargetRubyVersion: 3.1 + Exclude: + - db/schema.rb + - bin/* + +Documentation: + Enabled: false Metrics/LineLength: - Max: 120 + Max: 130 -Metrics/MethodLength: +Metrics/BlockLength: Max: 50 + ExcludedMethods: + - describe + - context + - path + - get + - patch + - put + - post + - delete + +Style/NumericPredicate: + Exclude: + - spec/**/* + +Metrics/AbcSize: + Max: 30 diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..c91ccd1 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,394 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2021-10-23 16:16:34 UTC using RuboCop version 1.22.2. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, IndentationWidth. +# SupportedStyles: with_first_argument, with_fixed_indentation +Layout/ArgumentAlignment: + Exclude: + - 'spec/processors/message_processor/eightball_spec.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/EmptyLineAfterGuardClause: + Exclude: + - 'app/models/pair.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +Layout/EmptyLines: + Exclude: + - 'config/application.rb' + - 'config/environments/development.rb' + - 'spec/models/pair_spec.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment. +Layout/ExtraSpacing: + Exclude: + - 'config/environments/production.rb' + - 'db/migrate/20170816162384_create_singles.rb' + - 'db/migrate/20210925130635_remove_urls_and_singles.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Layout/LeadingEmptyLines: + Exclude: + - 'spec/rails_helper.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. +# URISchemes: http, https +Layout/LineLength: + Max: 133 + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator. +# SupportedStylesForExponentOperator: space, no_space +Layout/SpaceAroundOperators: + Exclude: + - 'config/environments/production.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceBeforeBlockBraces: + Exclude: + - 'spec/models/pair_spec.rb' + +# Offense count: 10 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets. +# SupportedStyles: space, no_space, compact +# SupportedStylesForEmptyBrackets: space, no_space +Layout/SpaceInsideArrayLiteralBrackets: + Exclude: + - 'config/environments/production.rb' + - 'db/migrate/20170815155843_create_pairs.rb' + - 'db/migrate/20170816162384_create_singles.rb' + - 'db/migrate/20171104065321_create_data_banks.rb' + +# Offense count: 6 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters. +# SupportedStyles: space, no_space +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideBlockBraces: + Exclude: + - 'lib/tasks/databank.rake' + - 'spec/models/pair_spec.rb' + - 'spec/processors/message_processor/me_spec.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. +# SupportedStyles: space, no_space, compact +# SupportedStylesForEmptyBraces: space, no_space +Layout/SpaceInsideHashLiteralBraces: + Exclude: + - 'app/models/word.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: space, compact, no_space +Layout/SpaceInsideParens: + Exclude: + - 'db/migrate/20171028053628_remove_one_world_sentences.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: final_newline, final_blank_line +Layout/TrailingEmptyLines: + Exclude: + - 'config/application.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. +Lint/UnusedBlockArgument: + Exclude: + - 'lib/tasks/databank.rake' + +# Offense count: 1 +# Configuration parameters: IgnoredMethods, CountRepeatedAttributes. +Metrics/AbcSize: + Max: 36 + +# Offense count: 3 +# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. +Metrics/BlockLength: + Max: 127 + +# Offense count: 2 +# Configuration parameters: IgnoredMethods. +Metrics/CyclomaticComplexity: + Max: 9 + +# Offense count: 6 +# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. +Metrics/MethodLength: + Max: 23 + +# Offense count: 1 +# Configuration parameters: IgnoredMethods. +Metrics/PerceivedComplexity: + Max: 9 + +# Offense count: 1 +Naming/AccessorMethodName: + Exclude: + - 'spec/support/web_mocks/telegram.rb' + +# Offense count: 5 +# Cop supports --auto-correct. +# Configuration parameters: SkipBlocks, EnforcedStyle. +# SupportedStyles: described_class, explicit +RSpec/DescribedClass: + Exclude: + - 'spec/models/pair_spec.rb' + - 'spec/models/word_spec.rb' + +# Offense count: 4 +# Configuration parameters: CountAsOne. +RSpec/ExampleLength: + Max: 9 + +# Offense count: 7 +# Cop supports --auto-correct. +# Configuration parameters: Include. +# Include: spec/factories.rb, spec/factories/**/*.rb, features/support/factories/**/*.rb +RSpec/FactoryBot/FactoryClassName: + Exclude: + - 'spec/factories/chat.rb' + - 'spec/factories/participation.rb' + - 'spec/factories/telegram/chat.rb' + - 'spec/factories/telegram/chat_photo.rb' + - 'spec/factories/telegram/update.rb' + - 'spec/factories/telegram/user.rb' + - 'spec/factories/user.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: is_expected, should +RSpec/ImplicitExpect: + Exclude: + - 'spec/models/pair_spec.rb' + - 'spec/models/user_spec.rb' + +# Offense count: 7 +RSpec/MessageChain: + Exclude: + - 'spec/controllers/webhooks_controller_spec.rb' + - 'spec/processors/update_processor_spec.rb' + - 'spec/workers/message_worker_spec.rb' + - 'spec/workers/send_payload_worker_spec.rb' + - 'spec/workers/update_worker_spec.rb' + +# Offense count: 8 +RSpec/MultipleExpectations: + Max: 3 + +# Offense count: 46 +# Configuration parameters: AllowSubject. +RSpec/MultipleMemoizedHelpers: + Max: 13 + +# Offense count: 4 +RSpec/RepeatedExample: + Exclude: + - 'spec/processors/message_processor/start_spec.rb' + - 'spec/processors/message_processor/stop_spec.rb' + +# Offense count: 7 +# Cop supports --auto-correct. +RSpec/SingleArgumentMessageChain: + Exclude: + - 'spec/controllers/webhooks_controller_spec.rb' + - 'spec/processors/update_processor_spec.rb' + - 'spec/workers/message_worker_spec.rb' + - 'spec/workers/send_payload_worker_spec.rb' + - 'spec/workers/update_worker_spec.rb' + +# Offense count: 1 +RSpec/StubbedMock: + Exclude: + - 'spec/processors/update_processor_spec.rb' + +# Offense count: 3 +# Cop supports --auto-correct. +# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent. +Rails/Blank: + Exclude: + - 'lib/tasks/databank.rake' + +# Offense count: 2 +# Configuration parameters: Database, Include. +# SupportedDatabases: mysql, postgresql +# Include: db/migrate/*.rb +Rails/BulkChangeTable: + Exclude: + - 'db/migrate/20180203063658_add_dates_to_chat.rb' + +# Offense count: 9 +# Configuration parameters: Include. +# Include: db/migrate/*.rb +Rails/CreateTableWithTimestamps: + Exclude: + - 'db/migrate/20170815153040_create_chats.rb' + - 'db/migrate/20170815155629_create_words.rb' + - 'db/migrate/20170815155843_create_pairs.rb' + - 'db/migrate/20170815173153_create_urls.rb' + - 'db/migrate/20170816152384_create_replies.rb' + - 'db/migrate/20170816162384_create_singles.rb' + - 'db/migrate/20170817155650_create_greetings.rb' + - 'db/migrate/20170817161038_create_winners.rb' + - 'db/migrate/20171104065321_create_data_banks.rb' + +# Offense count: 4 +# Configuration parameters: EnforcedStyle, AllowToTime. +# SupportedStyles: strict, flexible +Rails/Date: + Exclude: + - 'app/models/winner.rb' + - 'app/processors/message_processor/eightball.rb' + +# Offense count: 4 +# Configuration parameters: EnforcedStyle. +# SupportedStyles: slashes, arguments +Rails/FilePath: + Exclude: + - 'config/environments/development.rb' + - 'config/initializers/locale.rb' + - 'lib/tasks/database.rake' + - 'spec/rails_helper.rb' + +# Offense count: 1 +# Configuration parameters: Include. +# Include: db/migrate/*.rb +Rails/ReversibleMigration: + Exclude: + - 'db/migrate/20180211095834_fix_chat_timestamps.rb' + +# Offense count: 10 +# Configuration parameters: ForbiddenMethods, AllowedMethods. +# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all +Rails/SkipsModelValidations: + Exclude: + - 'app/models/pair.rb' + - 'app/models/word.rb' + - 'db/migrate/20170917105603_persist_chat_i_ds.rb' + +# Offense count: 2 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: strict, flexible +Rails/TimeZone: + Exclude: + - 'db/migrate/20180203063658_add_dates_to_chat.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: always, conditionals +Style/AndOr: + Exclude: + - 'lib/tasks/database.rake' + +# Offense count: 5 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle. +# SupportedStyles: compact, expanded +Style/EmptyMethod: + Exclude: + - 'app/processors/message_processor/databank.rb' + - 'app/workers/covid_worker.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/GlobalStdStream: + Exclude: + - 'config/environments/production.rb' + +# Offense count: 1 +# Configuration parameters: MinBranchesCount. +Style/HashLikeCase: + Exclude: + - 'lib/tasks/database.rake' + +# Offense count: 2 +# Cop supports --auto-correct. +Style/KeywordParametersOrder: + Exclude: + - 'app/models/pair.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/Not: + Exclude: + - 'lib/tasks/database.rake' + +# Offense count: 3 +# Cop supports --auto-correct. +Style/SlicingWithRange: + Exclude: + - 'spec/processors/message_processor/base_processor_spec.rb' + - 'spec/processors/message_processor/me_spec.rb' + - 'spec/processors/message_processor/say_spec.rb' + +# Offense count: 1 +# Cop supports --auto-correct. +# Configuration parameters: Mode. +Style/StringConcatenation: + Exclude: + - 'lib/tasks/database.rake' + +# Offense count: 5 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. +# SupportedStyles: single_quotes, double_quotes +Style/StringLiterals: + Exclude: + - 'config.ru' + - 'config/environments/production.rb' + - 'config/initializers/backtrace_silencers.rb' + - 'lib/tasks/databank.rake' + - 'spec/factories/telegram/message.rb' + +# Offense count: 7 +# Cop supports --auto-correct. +# Configuration parameters: EnforcedStyle, MinSize. +# SupportedStyles: percent, brackets +Style/SymbolArray: + Exclude: + - 'config/initializers/filter_parameter_logging.rb' + - 'db/migrate/20170815155843_create_pairs.rb' + - 'db/migrate/20170816162384_create_singles.rb' + - 'db/migrate/20171104065321_create_data_banks.rb' + - 'lib/tasks/databank.rake' + +# Offense count: 1 +# Cop supports --auto-correct. +Style/WhileUntilDo: + Exclude: + - 'app/models/pair.rb' + +# Offense count: 4 +# Cop supports --auto-correct. +# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. +# URISchemes: http, https +Layout/LineLength: + Max: 133 diff --git a/.ruby-version b/.ruby-version index 437459c..fd2a018 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.5.0 +3.1.0 diff --git a/Capfile b/Capfile deleted file mode 100644 index 7d17719..0000000 --- a/Capfile +++ /dev/null @@ -1,10 +0,0 @@ -require 'capistrano/setup' -require 'capistrano/deploy' -require "capistrano/scm/git" -install_plugin Capistrano::SCM::Git - -require 'capistrano/rvm' -require 'capistrano/bundler' -require 'capistrano/rails/migrations' - -Dir.glob('lib/tasks/*.rake').each { |r| import r } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3304817 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ + +FROM ruby:3.1.1-alpine3.15 +EXPOSE 3000 +WORKDIR /opt/app/src +ENV LANG ru_RU.utf8 + +RUN apk add --no-cache --update git build-base ca-certificates less postgresql-dev postgresql-client tzdata make libffi-dev libxml2 libxml2-dev libxslt-dev && \ + cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime && \ + echo "Europe/Moscow" > /etc/timezone + +ARG CONTAINER_GID=1000 +ARG CONTAINER_UID=1000 + +RUN addgroup -g ${CONTAINER_GID} app && \ + adduser --uid ${CONTAINER_UID} --disabled-password --ingroup app --home /opt/app app && \ + chown -R app:app /opt/app && \ + chmod -R 0775 /opt/app + +USER app:app + +ADD --chown=app:app Gemfile Gemfile.lock /opt/app/src/ +RUN bundle config without production && bundle install --retry=3 --jobs 20 diff --git a/Gemfile b/Gemfile index cd1db29..eb6e5e1 100644 --- a/Gemfile +++ b/Gemfile @@ -1,39 +1,49 @@ -source 'https://rubygems.org' +# frozen_string_literal: true -ruby '2.5.0' +source 'https://rubygems.org' +git_source(:github) { |repo| "https://github.com/#{repo}.git" } -git_source(:github) do |repo_name| - repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/') - "https://github.com/#{repo_name}.git" -end +ruby '3.1.1' -gem 'newrelic_rpm' -gem 'pg', '~> 0.21' -gem 'puma', '~> 3.7' -gem 'rack-cors' -gem 'rails', '~> 5.1.3' +gem 'faraday' +gem 'pg' +gem 'puma' +gem 'rails' gem 'redis' +gem 'sentry-rails' +gem 'sentry-ruby' +gem 'sentry-sidekiq' gem 'sidekiq' -gem 'sidekiq-limit_fetch' -gem 'telegram-bot' -gem 'telegram-bot-types' -gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] - -group :development do - gem 'capistrano', '~> 3.6' - gem 'capistrano-rails', '~> 1.3' - gem 'capistrano-rvm' -end +gem 'sidekiq-cron' +gem 'sidekiq-limit_fetch', github: 'brainopia/sidekiq-limit_fetch', ref: 'bf9cee3b81e49ab8002bb9d564c2002d5b8a4b56' +gem 'telegram-bot-ruby' group :development, :test do + gem 'debug' + gem 'factory_bot' gem 'factory_bot_rails' gem 'ffaker' + gem 'guard' + gem 'guard-rake' + gem 'guard-rspec' gem 'listen' - gem 'localer' - gem 'pry' - gem 'pry-nav' - gem 'pry-rails' - gem 'rspec-its' + gem 'rack-test' + gem 'rspec' + gem 'rspec-mocks' gem 'rspec-rails' + gem 'shoulda-matchers' + gem 'webmock' +end + +group :development do gem 'rubocop' + gem 'rubocop-performance' + gem 'rubocop-rails' + gem 'rubocop-rspec' +end + +group :test do + gem 'database_cleaner' + gem 'rspec-sidekiq' + gem 'simplecov', require: false end diff --git a/Gemfile.lock b/Gemfile.lock index 7125c9b..4c1f916 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,262 +1,380 @@ +GIT + remote: https://github.com/brainopia/sidekiq-limit_fetch.git + revision: bf9cee3b81e49ab8002bb9d564c2002d5b8a4b56 + ref: bf9cee3b81e49ab8002bb9d564c2002d5b8a4b56 + specs: + sidekiq-limit_fetch (3.4.0) + sidekiq (>= 4) + GEM remote: https://rubygems.org/ specs: - actioncable (5.1.5) - actionpack (= 5.1.5) + actioncable (7.0.2.2) + actionpack (= 7.0.2.2) + activesupport (= 7.0.2.2) nio4r (~> 2.0) - websocket-driver (~> 0.6.1) - actionmailer (5.1.5) - actionpack (= 5.1.5) - actionview (= 5.1.5) - activejob (= 5.1.5) + websocket-driver (>= 0.6.1) + actionmailbox (7.0.2.2) + actionpack (= 7.0.2.2) + activejob (= 7.0.2.2) + activerecord (= 7.0.2.2) + activestorage (= 7.0.2.2) + activesupport (= 7.0.2.2) + mail (>= 2.7.1) + net-imap + net-pop + net-smtp + actionmailer (7.0.2.2) + actionpack (= 7.0.2.2) + actionview (= 7.0.2.2) + activejob (= 7.0.2.2) + activesupport (= 7.0.2.2) mail (~> 2.5, >= 2.5.4) + net-imap + net-pop + net-smtp rails-dom-testing (~> 2.0) - actionpack (5.1.5) - actionview (= 5.1.5) - activesupport (= 5.1.5) - rack (~> 2.0) + actionpack (7.0.2.2) + actionview (= 7.0.2.2) + activesupport (= 7.0.2.2) + rack (~> 2.0, >= 2.2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.1.5) - activesupport (= 5.1.5) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (7.0.2.2) + actionpack (= 7.0.2.2) + activerecord (= 7.0.2.2) + activestorage (= 7.0.2.2) + activesupport (= 7.0.2.2) + globalid (>= 0.6.0) + nokogiri (>= 1.8.5) + actionview (7.0.2.2) + activesupport (= 7.0.2.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.1.5) - activesupport (= 5.1.5) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + activejob (7.0.2.2) + activesupport (= 7.0.2.2) globalid (>= 0.3.6) - activemodel (5.1.5) - activesupport (= 5.1.5) - activerecord (5.1.5) - activemodel (= 5.1.5) - activesupport (= 5.1.5) - arel (~> 8.0) - activesupport (5.1.5) + activemodel (7.0.2.2) + activesupport (= 7.0.2.2) + activerecord (7.0.2.2) + activemodel (= 7.0.2.2) + activesupport (= 7.0.2.2) + activestorage (7.0.2.2) + actionpack (= 7.0.2.2) + activejob (= 7.0.2.2) + activerecord (= 7.0.2.2) + activesupport (= 7.0.2.2) + marcel (~> 1.0) + mini_mime (>= 1.1.0) + activesupport (7.0.2.2) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) - minitest (~> 5.1) - tzinfo (~> 1.1) - airbrussh (1.3.0) - sshkit (>= 1.6.1, != 1.7.0) - arel (8.0.0) - ast (2.4.0) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) + ast (2.4.2) axiom-types (0.1.1) descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) - builder (3.2.3) - capistrano (3.10.1) - airbrussh (>= 1.0.0) - i18n - rake (>= 10.0.0) - sshkit (>= 1.9.0) - capistrano-bundler (1.3.0) - capistrano (~> 3.1) - sshkit (~> 1.2) - capistrano-rails (1.3.1) - capistrano (~> 3.1) - capistrano-bundler (~> 1.1) - capistrano-rvm (0.1.2) - capistrano (~> 3.0) - sshkit (~> 1.2) - coderay (1.1.2) + builder (3.2.4) + coderay (1.1.3) coercible (1.0.0) descendants_tracker (~> 0.0.1) - concurrent-ruby (1.0.5) - connection_pool (2.2.1) - crass (1.0.3) + concurrent-ruby (1.1.9) + connection_pool (2.2.5) + crack (0.4.5) + rexml + crass (1.0.6) + database_cleaner (2.0.1) + database_cleaner-active_record (~> 2.0.0) + database_cleaner-active_record (2.0.1) + activerecord (>= 5.a) + database_cleaner-core (~> 2.0.0) + database_cleaner-core (2.0.1) + debug (1.4.0) + irb (>= 1.3.6) + reline (>= 0.2.7) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - diff-lcs (1.3) - dry-initializer (2.3.0) - equalizer (0.0.11) - erubi (1.7.1) - factory_bot (4.8.2) - activesupport (>= 3.0.0) - factory_bot_rails (4.8.2) - factory_bot (~> 4.8.2) - railties (>= 3.0.0) - ffaker (2.8.1) - ffi (1.9.23) - globalid (0.4.1) - activesupport (>= 4.2.0) - httpclient (2.8.3) - i18n (0.9.5) + diff-lcs (1.5.0) + digest (3.1.0) + docile (1.4.0) + dry-inflector (0.2.1) + erubi (1.10.0) + et-orbi (1.2.6) + tzinfo + factory_bot (6.2.0) + activesupport (>= 5.0.0) + factory_bot_rails (6.2.0) + factory_bot (~> 6.2.0) + railties (>= 5.0.0) + faraday (2.2.0) + faraday-net_http (~> 2.0) + ruby2_keywords (>= 0.0.4) + faraday-net_http (2.0.1) + ffaker (2.20.0) + ffi (1.15.5) + formatador (1.1.0) + fugit (1.5.2) + et-orbi (~> 1.1, >= 1.1.8) + raabro (~> 1.4) + globalid (1.0.0) + activesupport (>= 5.0) + guard (2.18.0) + formatador (>= 0.2.4) + listen (>= 2.7, < 4.0) + lumberjack (>= 1.0.12, < 2.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.13.0) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-compat (1.2.1) + guard-rake (1.0.0) + guard + rake + guard-rspec (4.7.3) + guard (~> 2.1) + guard-compat (~> 1.1) + rspec (>= 2.99.0, < 4.0) + hashdiff (1.0.1) + i18n (1.10.0) concurrent-ruby (~> 1.0) ice_nine (0.11.2) - listen (3.1.5) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - ruby_dep (~> 1.2) - localer (0.1.1) - dry-initializer (~> 2.3.0) - thor (~> 0.20.0) - loofah (2.2.1) + io-console (0.5.11) + io-wait (0.2.1) + irb (1.4.1) + reline (>= 0.3.0) + listen (3.7.1) + rb-fsevent (~> 0.10, >= 0.10.3) + rb-inotify (~> 0.9, >= 0.9.10) + loofah (2.14.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.7.0) + lumberjack (1.2.8) + mail (2.7.1) mini_mime (>= 0.1.1) - method_source (0.8.2) - mini_mime (1.0.0) - mini_portile2 (2.3.0) - minitest (5.11.3) - net-scp (1.2.1) - net-ssh (>= 2.6.5) - net-ssh (4.2.0) - newrelic_rpm (4.8.0.341) - nio4r (2.3.0) - nokogiri (1.8.2) - mini_portile2 (~> 2.3.0) - parallel (1.12.1) - parser (2.5.0.4) - ast (~> 2.4.0) - pg (0.21.0) - powerpack (0.1.1) - pry (0.10.4) - coderay (~> 1.1.0) - method_source (~> 0.8.1) - slop (~> 3.4) - pry-nav (0.2.4) - pry (>= 0.9.10, < 0.11.0) - pry-rails (0.3.6) - pry (>= 0.10.4) - puma (3.11.3) - rack (2.0.4) - rack-cors (1.0.2) - rack-protection (2.0.1) - rack - rack-test (0.8.3) + marcel (1.0.2) + method_source (1.0.0) + mini_mime (1.1.2) + mini_portile2 (2.8.0) + minitest (5.15.0) + nenv (0.3.0) + net-imap (0.2.3) + digest + net-protocol + strscan + net-pop (0.1.1) + digest + net-protocol + timeout + net-protocol (0.1.2) + io-wait + timeout + net-smtp (0.3.1) + digest + net-protocol + timeout + nio4r (2.5.8) + nokogiri (1.13.3) + mini_portile2 (~> 2.8.0) + racc (~> 1.4) + notiffany (0.1.3) + nenv (~> 0.1) + shellany (~> 0.0) + parallel (1.21.0) + parser (3.1.1.0) + ast (~> 2.4.1) + pg (1.3.3) + pry (0.14.1) + coderay (~> 1.1) + method_source (~> 1.0) + public_suffix (4.0.6) + puma (5.6.2) + nio4r (~> 2.0) + raabro (1.4.0) + racc (1.6.0) + rack (2.2.3) + rack-test (1.1.0) rack (>= 1.0, < 3) - rails (5.1.5) - actioncable (= 5.1.5) - actionmailer (= 5.1.5) - actionpack (= 5.1.5) - actionview (= 5.1.5) - activejob (= 5.1.5) - activemodel (= 5.1.5) - activerecord (= 5.1.5) - activesupport (= 5.1.5) - bundler (>= 1.3.0) - railties (= 5.1.5) - sprockets-rails (>= 2.0.0) + rails (7.0.2.2) + actioncable (= 7.0.2.2) + actionmailbox (= 7.0.2.2) + actionmailer (= 7.0.2.2) + actionpack (= 7.0.2.2) + actiontext (= 7.0.2.2) + actionview (= 7.0.2.2) + activejob (= 7.0.2.2) + activemodel (= 7.0.2.2) + activerecord (= 7.0.2.2) + activestorage (= 7.0.2.2) + activesupport (= 7.0.2.2) + bundler (>= 1.15.0) + railties (= 7.0.2.2) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) - railties (5.1.5) - actionpack (= 5.1.5) - activesupport (= 5.1.5) + rails-html-sanitizer (1.4.2) + loofah (~> 2.3) + railties (7.0.2.2) + actionpack (= 7.0.2.2) + activesupport (= 7.0.2.2) method_source - rake (>= 0.8.7) - thor (>= 0.18.1, < 2.0) - rainbow (3.0.0) - rake (12.3.0) - rb-fsevent (0.10.3) - rb-inotify (0.9.10) - ffi (>= 0.5.0, < 2) - redis (4.0.1) - rspec-core (3.7.1) - rspec-support (~> 3.7.0) - rspec-expectations (3.7.0) + rake (>= 12.2) + thor (~> 1.0) + zeitwerk (~> 2.5) + rainbow (3.1.1) + rake (13.0.6) + rb-fsevent (0.11.1) + rb-inotify (0.10.1) + ffi (~> 1.0) + redis (4.6.0) + regexp_parser (2.2.1) + reline (0.3.1) + io-console (~> 0.5) + rexml (3.2.5) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) - rspec-its (1.2.0) - rspec-core (>= 3.0.0) - rspec-expectations (>= 3.0.0) - rspec-mocks (3.7.0) + rspec-support (~> 3.11.0) + rspec-mocks (3.11.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.7.0) - rspec-rails (3.7.2) - actionpack (>= 3.0) - activesupport (>= 3.0) - railties (>= 3.0) - rspec-core (~> 3.7.0) - rspec-expectations (~> 3.7.0) - rspec-mocks (~> 3.7.0) - rspec-support (~> 3.7.0) - rspec-support (3.7.1) - rubocop (0.53.0) + rspec-support (~> 3.11.0) + rspec-rails (5.1.0) + actionpack (>= 5.2) + activesupport (>= 5.2) + railties (>= 5.2) + rspec-core (~> 3.10) + rspec-expectations (~> 3.10) + rspec-mocks (~> 3.10) + rspec-support (~> 3.10) + rspec-sidekiq (3.1.0) + rspec-core (~> 3.0, >= 3.0.0) + sidekiq (>= 2.4.0) + rspec-support (3.11.0) + rubocop (1.25.1) parallel (~> 1.10) - parser (>= 2.5) - powerpack (~> 0.1) + parser (>= 3.1.0.0) rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml + rubocop-ast (>= 1.15.1, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (~> 1.0, >= 1.0.1) - ruby-progressbar (1.9.0) - ruby_dep (1.5.0) - sidekiq (5.1.1) - concurrent-ruby (~> 1.0) - connection_pool (~> 2.2, >= 2.2.0) - rack-protection (>= 1.5.0) - redis (>= 3.3.5, < 5) - sidekiq-limit_fetch (3.4.0) - sidekiq (>= 4) - slop (3.6.0) - sprockets (3.7.1) - concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.1) - actionpack (>= 4.0) - activesupport (>= 4.0) - sprockets (>= 3.0.0) - sshkit (1.16.0) - net-scp (>= 1.1.2) - net-ssh (>= 2.8.0) - telegram-bot (0.13.0) - actionpack (>= 4.0, < 6.0) - activesupport (>= 4.0, < 6.0) - httpclient (~> 2.7) - telegram-bot-types (0.5.0) - activesupport - virtus (~> 1.0) - thor (0.20.0) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.16.0) + parser (>= 3.1.1.0) + rubocop-performance (1.13.3) + rubocop (>= 1.7.0, < 2.0) + rubocop-ast (>= 0.4.0) + rubocop-rails (2.13.2) + activesupport (>= 4.2.0) + rack (>= 1.1) + rubocop (>= 1.7.0, < 2.0) + rubocop-rspec (2.9.0) + rubocop (~> 1.19) + ruby-progressbar (1.11.0) + ruby2_keywords (0.0.5) + sentry-rails (5.1.1) + railties (>= 5.0) + sentry-ruby-core (~> 5.1.1) + sentry-ruby (5.1.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + sentry-ruby-core (= 5.1.1) + sentry-ruby-core (5.1.1) + concurrent-ruby + sentry-sidekiq (5.1.1) + sentry-ruby-core (~> 5.1.1) + sidekiq (>= 3.0) + shellany (0.0.1) + shoulda-matchers (5.1.0) + activesupport (>= 5.2.0) + sidekiq (6.4.1) + connection_pool (>= 2.2.2) + rack (~> 2.0) + redis (>= 4.2.0) + sidekiq-cron (1.2.0) + fugit (~> 1.1) + sidekiq (>= 4.2.1) + simplecov (0.21.2) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.12.3) + simplecov_json_formatter (0.1.4) + strscan (3.0.1) + telegram-bot-ruby (0.16.0) + dry-inflector + faraday + virtus (~> 2.0) + thor (1.2.1) thread_safe (0.3.6) - tzinfo (1.2.5) - thread_safe (~> 0.1) - unicode-display_width (1.3.0) - virtus (1.0.5) + timeout (0.2.0) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) + unicode-display_width (2.1.0) + virtus (2.0.0) axiom-types (~> 0.1) coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) - equalizer (~> 0.0, >= 0.0.9) - websocket-driver (0.6.5) + webmock (3.14.0) + addressable (>= 2.8.0) + crack (>= 0.3.2) + hashdiff (>= 0.4.0, < 2.0.0) + websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.3) + websocket-extensions (0.1.5) + zeitwerk (2.5.4) PLATFORMS ruby DEPENDENCIES - capistrano (~> 3.6) - capistrano-rails (~> 1.3) - capistrano-rvm + database_cleaner + debug + factory_bot factory_bot_rails + faraday ffaker + guard + guard-rake + guard-rspec listen - localer - newrelic_rpm - pg (~> 0.21) - pry - pry-nav - pry-rails - puma (~> 3.7) - rack-cors - rails (~> 5.1.3) + pg + puma + rack-test + rails redis - rspec-its + rspec + rspec-mocks rspec-rails + rspec-sidekiq rubocop + rubocop-performance + rubocop-rails + rubocop-rspec + sentry-rails + sentry-ruby + sentry-sidekiq + shoulda-matchers sidekiq - sidekiq-limit_fetch - telegram-bot - telegram-bot-types - tzinfo-data + sidekiq-cron + sidekiq-limit_fetch! + simplecov + telegram-bot-ruby + webmock RUBY VERSION - ruby 2.5.0p0 + ruby 3.1.1p18 BUNDLED WITH - 1.16.1 + 2.3.7 diff --git a/Guardfile b/Guardfile new file mode 100644 index 0000000..c11ca6a --- /dev/null +++ b/Guardfile @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +guard :rspec, cmd: 'bundle exec rspec' do + require 'guard/rspec/dsl' + dsl = Guard::RSpec::Dsl.new(self) + + rspec = dsl.rspec + watch(rspec.spec_helper) { rspec.spec_dir } + watch(rspec.spec_support) { rspec.spec_dir } + watch(rspec.spec_files) + + ruby = dsl.ruby + dsl.watch_spec_files_for(ruby.lib_files) + + rails = dsl.rails + dsl.watch_spec_files_for(rails.app_files) + dsl.watch_spec_files_for(rails.views) + + watch(rails.controllers) { |m| [rspec.spec.call("controllers/#{m[1]}_controller")] } + watch(rails.controllers) { |m| [rspec.spec.call("request/#{m[1]}")] } + + watch(rails.spec_helper) { rspec.spec_dir } + watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" } +end diff --git a/README.md b/README.md index 3b410fb..9642ef9 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,3 @@ -Shizoid chat bot for telegram. Inspired by [Sulci](https://github.com/ermine/sulci) Bot. - -Sorry guys, I can't give my instance for everyone for now. +![Shizoid](https://github.com/top4ek/shizoid-ng/workflows/Shizoid/badge.svg?branch=master) -You can run your own ;) -1. `cap production deploy:setup` -1. edit configs -1. `cap production deploy` -1. `RAILS_ENV=production bundle exec rake db:create` -1. and :) `cap production deploy` +Shizoid chat bot for telegram. Inspired by [Sulci](https://github.com/ermine/sulci) Bot. diff --git a/Rakefile b/Rakefile index e85f913..6d1041e 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,4 @@ -# Add your own tasks in files placed in lib/tasks ending in .rake, -# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. +# frozen_string_literal: true require_relative 'config/application' diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb deleted file mode 100644 index 4ac8823..0000000 --- a/app/controllers/application_controller.rb +++ /dev/null @@ -1,2 +0,0 @@ -class ApplicationController < ActionController::API -end diff --git a/app/controllers/concerns/bayanizator.rb b/app/controllers/concerns/bayanizator.rb deleted file mode 100644 index 14ea1aa..0000000 --- a/app/controllers/concerns/bayanizator.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Bayanizator - def bayanizator(*args) - return unless can_reply? - reply_text = nok - case args.first - when 'enable', 'on' - @chat.update(bayan: true) - reply_text = ok - when 'disable', 'off' - @chat.update(bayan: false) - reply_text = ok - else - reply_text = t('.bayanizator.size', size: Url.all.size) - end - reply_with :message, text: reply_text - end - - def bayan? - return unless text? && @chat.bayan? - urls = payload.entities.select { |entity| entity.type == 'url' } - .map { |entity| payload.text[entity.offset, entity.length] } - return if urls.empty? - urls.select { |url| Url.seen? url }.any? - end -end diff --git a/app/controllers/concerns/cool_story.rb b/app/controllers/concerns/cool_story.rb deleted file mode 100644 index 1c016e3..0000000 --- a/app/controllers/concerns/cool_story.rb +++ /dev/null @@ -1,11 +0,0 @@ -module CoolStory - def cool_story(*) - return unless can_reply? - reply_text = t('.').sample - return reply_with :message, text: reply_text unless @chat.random_answer?(40) - send_typing_action - reply = @chat.generate_story - return reply_with :message, text: reply if reply.present? - reply_with :message, text: reply_text - end -end diff --git a/app/controllers/concerns/databanks.rb b/app/controllers/concerns/databanks.rb deleted file mode 100644 index 37cc62a..0000000 --- a/app/controllers/concerns/databanks.rb +++ /dev/null @@ -1,30 +0,0 @@ -module Databanks - def databank(*args) - return unless can_reply? - databank_id = args.second.to_i - case args.first - when 'enable', 'on' - if DataBank.exists?(id: databank_id) && !@chat.data_bank_ids.include?(databank_id) - @chat.data_bank_ids << databank_id - @chat.save - reply_text = ok - else - reply_text = nok - end - when 'disable', 'off' - if @chat.data_bank_ids.include? databank_id - @chat.data_bank_ids -= [databank_id] - @chat.save - reply_text = ok - else - reply_text = nok - end - else - databanks = DataBank.pluck(:id, :name) - list = databanks.map { |d| t('.databank.list_line_html', id: d.first, name: d.second) }.join("\n") - active = @chat.data_bank_ids.present? ? @chat.data_bank_ids.to_sentence : t('false') - reply_text = t('.databank.list_html', list: list, active: active) - end - respond_with :message, text: reply_text, parse_mode: :html - end -end diff --git a/app/controllers/concerns/eightball.rb b/app/controllers/concerns/eightball.rb deleted file mode 100644 index 9adabe7..0000000 --- a/app/controllers/concerns/eightball.rb +++ /dev/null @@ -1,23 +0,0 @@ -module Eightball - def eightball(*text) - return unless can_reply? - reply_text = t('shizoid.eightball.empty').sample - return reply_with :message, text: reply_text if text.empty? - case text.first - when 'enable', 'on' - @chat.update(eightball: true) - reply_text = ok - when 'disable', 'off' - @chat.update(eightball: false) - reply_text = ok - else - answers = t('shizoid.eightball.answers') - message = payload.text.downcase - digest = Digest::SHA1.hexdigest(message).to_i(16) - Date.today.to_time.to_i.div(100) - from.id - answer_id = digest.divmod(answers.count)[1] - send_typing_action - reply_text = "#{answers[answer_id]} #{@chat.generate(@words)}" - end - reply_with :message, text: reply_text - end -end diff --git a/app/controllers/concerns/gab.rb b/app/controllers/concerns/gab.rb deleted file mode 100644 index e33ef85..0000000 --- a/app/controllers/concerns/gab.rb +++ /dev/null @@ -1,16 +0,0 @@ -module Gab - def gab(*args) - return unless can_reply? - reply_text = t('.level', chance: @chat.random) - if args.first.present? - percent = args.first.to_i - if percent >= 0 && percent < 51 - @chat.update(random: percent) - reply_text = "#{ok} #{t('.level', chance: @chat.random)}" - else - reply_text = t('.error') - end - end - respond_with :message, text: reply_text, parse_mode: :markdown - end -end diff --git a/app/controllers/concerns/greeting.rb b/app/controllers/concerns/greeting.rb deleted file mode 100644 index 4e3e617..0000000 --- a/app/controllers/concerns/greeting.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Greeting - def greeting(*args) - return unless can_reply? - # reply_text = t('.status.locale') - case args.first - when 'add' - when 'delete' - when 'clear' - when 'list' - when 'show' - else - - end - respond_with :message, text: reply_text - return unless can_reply? - respond_with :message, text: nok - end -end diff --git a/app/controllers/concerns/help.rb b/app/controllers/concerns/help.rb deleted file mode 100644 index 3a0f103..0000000 --- a/app/controllers/concerns/help.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Help - def help(*) - respond_with :message, text: t('.help'), parse_mode: :markdown - end -end diff --git a/app/controllers/concerns/helper_methods.rb b/app/controllers/concerns/helper_methods.rb deleted file mode 100644 index 7b3587a..0000000 --- a/app/controllers/concerns/helper_methods.rb +++ /dev/null @@ -1,77 +0,0 @@ -module HelperMethods - def ok - t('.ok').sample - end - - def nok - t('.nok').sample - end - - def send_typing_action - bot.send_chat_action(chat_id: @chat.telegram_id, action: :typing) - end - - def new_member? - payload.new_chat_members.present? - end - - def anchors? - text? && ((t('.anchors') & @words).any? || @words.include?("@#{bot.username.downcase}")) - end - - def can_delete? - begin - result = bot.async(false) do - bot.get_chat_member(chat_id: @chat.telegram_id, - user_id: bot_id)['result']['can_delete_messages'] - end - rescue - result = false - end - result - end - - def bot_id - @bot_id ||= Rails.application.secrets.telegram[:bot][:token].split(':').first.to_i - end - - def reply_to_bot? - payload&.reply_to_message&.from&.id == bot_id - end - - def can_reply? - admin? || @chat.enabled? - end - - def question? - text? && @text.last == '?' - end - - def sticker? - payload.sticker.present? - end - - def document? - payload.document.present? - end - - def text? - payload.text.present? - end - - def reply? - payload.reply_to_message.present? - end - - def command? - text? && payload.text.chars.first == '/' - end - - def admin? - Rails.application.secrets.admins.include? from.id - end - - def private_admin? - Rails.application.secrets.admins.include? @chat.telegram_id - end -end diff --git a/app/controllers/concerns/ids.rb b/app/controllers/concerns/ids.rb deleted file mode 100644 index d0b60a8..0000000 --- a/app/controllers/concerns/ids.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Ids - def ids(*) - return unless can_reply? - text = t('.ids', user: from.id, chat: @chat.telegram_id, type: @chat.kind) - respond_with :message, text: text, parse_mode: :markdown - end -end diff --git a/app/controllers/concerns/locale.rb b/app/controllers/concerns/locale.rb deleted file mode 100644 index 6b3f763..0000000 --- a/app/controllers/concerns/locale.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Locale - def locale(*args) - return unless can_reply? - reply_text = t('.reply', - available: I18n.available_locales.to_sentence, - current: t('.current')) - parameter = args.first - if parameter.present? - if parameter.in? I18n.available_locales.map(&:to_s) - @chat.update(locale: parameter) - I18n.locale = parameter - reply_text = t('.current') - else - reply_text = nok - end - end - reply_with :message, text: reply_text, parse_mode: :markdown - end -end diff --git a/app/controllers/concerns/me.rb b/app/controllers/concerns/me.rb deleted file mode 100644 index f1d6f5f..0000000 --- a/app/controllers/concerns/me.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Me - def me(*text) - return unless can_reply? - reply = "#{from.username || from.first_name || from.last_name} " - reply += text.any? ? text.join(' ') : t('.').sample - respond_with :message, text: reply, parse_mode: :html - bot.delete_message(chat_id: @chat.telegram_id, message_id: payload.message_id) if can_delete? - end -end diff --git a/app/controllers/concerns/ping.rb b/app/controllers/concerns/ping.rb deleted file mode 100644 index 48a9d75..0000000 --- a/app/controllers/concerns/ping.rb +++ /dev/null @@ -1,5 +0,0 @@ -module Ping - def ping(*) - respond_with :message, text: t('.ping').sample if can_reply? - end -end diff --git a/app/controllers/concerns/say.rb b/app/controllers/concerns/say.rb deleted file mode 100644 index 6edce62..0000000 --- a/app/controllers/concerns/say.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Say - def say(*args) - return unless admin? && can_delete? - respond_with :message, text: args.join(' '), parse_mode: :markdown - bot.delete_message(chat_id: @chat.telegram_id, message_id: payload.message_id) - end -end diff --git a/app/controllers/concerns/status.rb b/app/controllers/concerns/status.rb deleted file mode 100644 index 573a3f0..0000000 --- a/app/controllers/concerns/status.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Status - def status(*) - return unless can_reply? - respond_with :message, parse_mode: :markdown, - text: t('.reply', - active: t(@chat.enabled?.to_s), - gab: @chat.random, - pairs: @chat.pairs.size, - databanks: t(@chat.data_bank_ids.present?.to_s), - singles: @chat.singles.size, - auto_eightball: t(@chat.eightball?.to_s), - winner: @chat.winner || t('.winner.disabled'), - check_bayan: t(@chat.bayan?.to_s)) - end -end diff --git a/app/controllers/concerns/winner_of_the_day.rb b/app/controllers/concerns/winner_of_the_day.rb deleted file mode 100644 index bc5391f..0000000 --- a/app/controllers/concerns/winner_of_the_day.rb +++ /dev/null @@ -1,52 +0,0 @@ -module WinnerOfTheDay - def winner(*args) - return unless can_reply? - reply_text = t('.winner.no_one') - case args.first - when 'enable', 'on' - if args.second.present? - @chat.update(winner: args[1..-1].join(' ')) - else - @chat.update(winner: t('.winner.default')) - end - reply_text = ok - when 'disable', 'off' - @chat.update(winner: nil) - reply_text = ok - when 'me' - top = Winner.stats(@chat.id, from.id) - if top[:dates].present? - reply_text = t('.winner.user.top', name: @chat.winner, dates: top[:dates].join("\n"), count: top[:count]) - else - reply_text = t('.winner.user.none').sample - end - when 'current' - current_stats = Winner.current_stats(@chat.id).sort_by { |_, value| value }.reverse[0..9].to_h - names = Chat.names(current_stats.keys) - stats = current_stats.map do |user, count| - [names[user] || fetch_user_info(@chat.telegram_id, user) || t('.winner.user.unknown'), count] - end - top = stats.each_with_index.map do |s, i| - t('.winner.current_top_line_html', user: s.first, count: s.second, position: i + 1) - end - reply_text = t('.winner.current_html', top: top.join("\n")) - else - gambled = Winner.gambled?(@chat.id) - winner_id = Winner.gamble(@chat.id) - if winner_id.present? - stats = Winner.stats(@chat.id) - names = Chat.names(stats.keys) - user_chat = Chat.find_by(telegram_id: winner_id) - user = gambled ? user_chat.to_s : user_chat.to_link - stats = stats.map do |user, count| - [names[user] || fetch_user_info(@chat.telegram_id, user) || t('.winner.user.unknown'), count] - end - top = stats.each_with_index.map do |s, i| - t('.winner.top_line_html', user: s.first, count: s.second, position: i + 1) - end - reply_text = t('.winner.day_html', name: @chat.winner, user: user, top: top.join("\n")) - end - end - respond_with :message, text: reply_text, parse_mode: :html - end -end diff --git a/app/controllers/shizoid_controller.rb b/app/controllers/shizoid_controller.rb deleted file mode 100644 index dadccec..0000000 --- a/app/controllers/shizoid_controller.rb +++ /dev/null @@ -1,123 +0,0 @@ -class ShizoidController < Telegram::Bot::UpdatesController - include Telegram::Bot::Botan::ControllerHelpers - include Telegram::Bot::UpdatesController::TypedUpdate - include HelperMethods - include Eightball - include Databanks - include Gab - include Status - include CoolStory - include Locale - include Help - include Me - include Ids - include Ping - include WinnerOfTheDay - include Bayanizator - # include Greeting - - before_action :fetch_data - after_action :learn - - def start(*args) - return respond_with :message, text: nok unless admin? - @chat.enable! - respond_with :message, text: ok - end - - def stop(*) - return reply_with :message, text: nok unless admin? - begin - bot.async(false) { respond_with :message, text: ok } - rescue - NewRelic::Agent.notice_error('Forbidden', custom_params: { chat: @chat.id }) - end - @chat.leave! unless @chat.personal? - end - - def message(*text) - return unless can_reply? && !payload.from.is_bot - if @chat.winner.present? - count = 1 - count = @words.uniq.size if @words.present? && @words.size > 1 - StatsUpdater.perform_async(id: @chat.id, from: from.id, count: count) - winner(text) unless Winner.gambled?(@chat.id) - end - return reply_with(:message, text: t('.bayan').sample) if bayan? - return eightball(text) if question? && @chat.eightball? - return unless anchors? || reply_to_bot? || @chat.random_answer? || @chat.personal? - return unless text? && !command? - return reply_single if @words.size == 1 - send_typing_action - reply = @chat.generate(@words) - respond_with :message, text: reply if reply.present? - end - - private - - def fetch_user_info(chat_id, user_id) - begin - result = bot.async(false) { bot.get_chat_member(chat_id: chat_id, user_id: user_id) } - rescue - result = nil - end - unless result.present? && result['ok'] && result['result']['user'].present? - NewRelic::Agent.notice_error('UserNotFetched', custom_params: { chat: chat_id, user: user_id, result: result }) - return nil - end - user_data = result['result']['user'] - user = Chat.find_by(telegram_id: user_id) || Chat.create(telegram_id: user_id, kind: :personal) - options = { id: user_id, title: nil, kind: 'private', first_name: user_data['first_name'], - last_name: user_data['last_name'], username: user_data['username'] } - ChatUpdater.perform_async(options) - "#{user_data['username'] || user_data['first_name'] || user_data['last_name']}" - end - - def reply_single - reply = Single.answer(@chat.id, @words.first) - return unless reply.present? - case reply.reply_type - when 'text' - respond_with :message, text: reply.reply - send_typing_action - words = Word.to_ids(reply.reply.downcase.split(' ').uniq) - text_reply = @chat.generate(words) - respond_with :message, text: text_reply if text_reply.present? - when 'sticker' - respond_with :sticker, sticker: reply.reply - when 'document' - respond_with :document, document: reply.reply - end - end - - def fetch_data - @chat = Chat.learn payload - @chat.disable! if payload&.left_chat_member&.id == bot_id - I18n.locale = @chat.locale - return unless text? - text = payload.text.downcase.dup - payload.entities.each do |entity| - if entity.offset + entity.length > text.size - NewRelic::Agent.notice_error('PayloadShort', - expected: true, - custom_params: { chat: @chat.id, - payload: payload.to_json }) - next - end - text[entity.offset, entity.length] = ' ' * entity.length unless entity.type == 'bold' - end - @words = text.split(' ') - @text = @words.join(' ') - end - - def learn - if @chat.enabled? && !payload.from.is_bot && !command? && !(@chat.eightball? && question?) - if text? && @words.size > 1 - options = { id: @chat.id, words: @words } - PairUpdater.perform_async(options) - ContextUpdater.perform_async(options) - end - Single.learn(@chat.id, payload) if reply? - end - end -end diff --git a/app/controllers/webhooks_controller.rb b/app/controllers/webhooks_controller.rb new file mode 100644 index 0000000..2f5e13d --- /dev/null +++ b/app/controllers/webhooks_controller.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +class WebhooksController < ActionController::API + def update + render result + end + + def ping + render status: :ok, json: 'ok' + end + + private + + def result + return { status: :not_found } unless token_valid? + + UpdateWorker.perform_async request_params + { status: :ok, json: {} } + end + + def request_params + params.permit!.to_h # (:update_id, message: {}) + end + + def token_valid? + params[:id] == TelegramService.token_secret + end +end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb deleted file mode 100644 index a009ace..0000000 --- a/app/jobs/application_job.rb +++ /dev/null @@ -1,2 +0,0 @@ -class ApplicationJob < ActiveJob::Base -end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index 10a4cba..71fbba5 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ApplicationRecord < ActiveRecord::Base self.abstract_class = true end diff --git a/app/models/chat.rb b/app/models/chat.rb index 251dc61..be88b19 100644 --- a/app/models/chat.rb +++ b/app/models/chat.rb @@ -1,25 +1,38 @@ +# frozen_string_literal: true + class Chat < ApplicationRecord + has_many :participations, dependent: :destroy has_many :pairs, dependent: :destroy - has_many :participations - has_many :participants, through: :participations - - # has_many :greetings, dependent: :destroy - has_many :singles, dependent: :destroy + has_many :users, through: :participations has_many :winners, dependent: :destroy - has_many :urls scope :inactive, -> { where(active_at: nil) } scope :active, -> { where.not(active_at: nil) } - scope :not_personal, -> { where.not(kind: :personal) } + scope :not_personal, -> { where.not(kind: 'private') } + scope :with_winners, -> { where.not(winner: nil) } - enum kind: %i[personal faction supergroup channel] + enum covid_region: CovidStat.regions + + def winner_enabled? + winner.present? + end - def to_s - "#{(username || first_name || last_name || title)}" + def winner_disabled? + !winner_enabled? end - def to_link - "#{to_s}" + def current_winner + winners.find_by(created_at: Time.zone.today) + end + + def choose_winner! + scores = participations.scored.order(score: :desc) + winner = scores.limit(3).sample&.user + return nil if winner.blank? + + winners.create(user: winner) + scores.each { |p| p.update!(score: 0) } + winner end def enabled? @@ -31,97 +44,51 @@ def disabled? end def enable! - update(active_at: Time.now) + update!(active_at: Time.current) end def disable! - update(active_at: nil) + update!(active_at: nil) end - def random_answer?(additional = 0) + def self.learn(message) + chat_payload = message.chat + chat = Chat.find_or_initialize_by(telegram_id: chat_payload.id) + chat.telegram_id = message.migrate_to_chat_id if message.migrate_to_chat_id.present? + chat.kind = chat_payload.type + chat.update(chat_payload.to_h.slice(:title, :username, :first_name, :last_name)) + chat + end + + def answer_randomly?(additional = 0) rand(100) < (random + additional) end - def generate_story - Pair.generate_story(self) + def generate_reply(words) + Pair.build_sentence(chat: self, words: words) || Pair.build_sentence(chat: self, words_ids: context) end - def generate(words) - Pair.generate(chat: self, words: words) + def generate_story + context.collect { Pair.build_sentence(chat: self, words_ids: context) }.uniq.join('. ') end def context(ids = nil) - size = Rails.application.secrets.context_size - current = Shizoid::Redis.connection.lrange(redis_context_path, 0, size).map(&:to_i) + size = Rails.configuration.secrets[:context_size] + current = RedisService.lrange(redis_context_path, 0, size).map(&:to_i) return current.shuffle if ids.nil? + uniq_ids = ids.uniq current -= uniq_ids current.unshift(*uniq_ids) - Shizoid::Redis.connection.multi do |r| + RedisService.multi do |r| r.del(redis_context_path) r.lpush(redis_context_path, current.first(size)) end end - def leave! - disable! - return if personal? - bot = Telegram::Bot::Client.new(Rails.application.secrets.telegram[:bot][:token]) - begin - bot.async(false) { bot.leave_chat(chat_id: telegram_id) } - rescue - NewRelic::Agent.notice_error('UnableToLeave', custom_params: { chat: id }) - end - end - - def self.names(ids) - Chat.where(telegram_id: ids).map { |n| [n.telegram_id, n.to_s] }.to_h - end - - def self.learn(payload) - chat = Chat.find_by(telegram_id: payload.chat.id) || Chat.new(telegram_id: payload.chat.id, - kind: adopt_type(payload.chat.type)) - chat.telegram_id = payload.migrate_to_chat_id unless payload.migrate_to_chat_id.nil? - chat.save - options = { id: chat.id, title: payload.chat.title, first_name: payload.chat.first_name, - last_name: payload.chat.last_name, username: payload.chat.username, kind: payload.chat.type } - ChatUpdater.perform_async(options) - - if payload.from.present? && payload.chat.id != payload.from.id - user = Chat.find_by(telegram_id: payload.from.id) || Chat.create(telegram_id: payload.from.id, kind: :personal) - options = { id: user.id, title: nil, kind: 'private', first_name: payload.from.first_name, - last_name: payload.from.last_name, username: payload.from.username } - ChatUpdater.perform_async(options) - options = { chat_id: chat.telegram_id, user_id: user.telegram_id, left_id: payload&.left_chat_member&.id } - ParticipantUpdater.perform_async(options) - end - chat - end - - def update_meta(title:, first_name:, last_name:, username:, kind:) - self.title = title if self.title != title - self.first_name = first_name if self.first_name != first_name - self.last_name = last_name if self.last_name != last_name - self.username = username if self.username != username - self.kind = self.class.adopt_type(kind) if self.kind != self.class.adopt_type(kind) - self.active_at = DateTime.now if enabled? - self.save - end - private def redis_context_path "chat_context/#{id}" end - - def self.adopt_type(type) - case type - when 'private' - :personal - when 'group' - :faction - else - type.to_sym - end - end end diff --git a/app/models/covid_stat.rb b/app/models/covid_stat.rb new file mode 100644 index 0000000..7d4b35f --- /dev/null +++ b/app/models/covid_stat.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +class CovidStat < ApplicationRecord + enum region: { + ALL: 0, + AD: 2, ALT: 3, AMU: 4, ARK: 5, AST: 6, BA: 7, BEL: 8, BRY: 9, BU: 10, CE: 11, CHE: 12, CHU: 13, CR: 14, CU: 15, DA: 16, + GA: 17, IN: 18, IRK: 19, IVA: 20, KAM: 21, KB: 22, KC: 23, KDA: 24, KEM: 25, KGD: 26, KGN: 27, KHA: 28, KHM: 29, KIR: 30, + KK: 31, KL: 32, KLU: 33, KO: 34, KOS: 35, KR: 36, KRS: 37, KYA: 38, LEN: 39, LIP: 40, MAG: 41, ME: 42, MO: 43, MOS: 44, + MOW: 45, MUR: 46, NEN: 47, NGR: 48, NIZ: 49, NVS: 50, OMS: 51, ORE: 52, ORL: 53, PER: 54, PNZ: 55, PRI: 56, PSK: 57, + ROS: 58, RYA: 59, SA: 60, SAK: 61, SAM: 62, SAR: 63, SE: 64, SMO: 65, SPE: 66, STA: 67, SVE: 68, TAM: 69, TOM: 70, TT: 71, + TUL: 72, TVE: 73, TY: 74, TYU: 75, UD: 76, ULY: 77, VGG: 78, VLA: 79, VLG: 80, VOR: 81, YAN: 82, YAR: 83, YEV: 84, ZAB: 85 + } + + VIRTUAL_REGIONS = ['ALL'].freeze + FETCHABLE_REGIONS = (regions.keys - VIRTUAL_REGIONS).freeze + STATISTICS_ATTRIBUTES = %i[sick healed died].freeze + + validates :region, inclusion: { in: FETCHABLE_REGIONS } + + class << self + def day_stats(region: 'ALL', date: nil, return_i18n: false) + date ||= CovidStat.order(date: :desc).first.date + + select_params = STATISTICS_ATTRIBUTES.map { |a| "SUM(#{a}) as #{a}" }.join(',') + current_stats, previous_stats = [date, date - 1.day].map do |d| + query = { date: d } + query[:region] = region if region != 'ALL' + CovidStat.where(query).select(select_params) + end.flatten + + results = { date: date, region: region, current: current_stats } + results[:delta] = STATISTICS_ATTRIBUTES.map do |a| + previous_param = previous_stats.send(a) + current_param = current_stats.send(a) + + result = if current_param.nil? || previous_param.nil? + '?' + else + current_param - previous_param + end + [a, result] + end.to_h + if return_i18n + printable_results = STATISTICS_ATTRIBUTES.map do |a| + current = results[:current].send(a) || '?' + delta = results[:delta][a] + delta = if delta.present? + delta.to_i.positive? ? "+#{delta}" : delta.to_s + else + '?' + end + [a, "#{current} (#{delta})"] + end.to_h + + I18n.t('covid_stat.stats_html', region: I18n.t("covid_stat.regions.#{region}"), + date: date, + sick: printable_results[:sick], + healed: printable_results[:healed], + died: printable_results[:died]) + else + results + end + end + + def needs_update? + sample_region = FETCHABLE_REGIONS.sample + remote_max_date = StopCovidService.get_data(sample_region).map { |r| r[:date] }.max + local_max_date = CovidStat.where(region: sample_region).maximum(:date) + return true if local_max_date.blank? + + remote_max_date > local_max_date + end + + def update_all + FETCHABLE_REGIONS.each { |r| CovidRegionUpdaterWorker.perform_async(r) } + end + + def update(region_id) + StopCovidService.get_data(region_id).map do |r| + record_key = { date: r[:date], region: region_id } + stat_record = CovidStat.find_or_initialize_by(record_key) + stat_record.update(r.except(:date, :region)) + stat_record.save! + stat_record + end + end + end +end diff --git a/app/models/data_bank.rb b/app/models/data_bank.rb index c8f6e1d..337db59 100644 --- a/app/models/data_bank.rb +++ b/app/models/data_bank.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DataBank < ApplicationRecord has_many :pairs, dependent: :destroy diff --git a/app/models/greeting.rb b/app/models/greeting.rb deleted file mode 100644 index 5e3e4b0..0000000 --- a/app/models/greeting.rb +++ /dev/null @@ -1,15 +0,0 @@ -class Greeting < ApplicationRecord - belongs_to :chat - - def self.fetch - get.sample - end - - def self.list(chat_id) - Greetings.where(chat_id: chat_id).pluck(:id, :text) - end - - def self.add(chat_id, greeting) - Greetings.create(chat_id: chat_id, text: greeting) - end -end diff --git a/app/models/pair.rb b/app/models/pair.rb index 9e2bedc..2ebf3d7 100644 --- a/app/models/pair.rb +++ b/app/models/pair.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Pair < ApplicationRecord belongs_to :first, class_name: 'Word', optional: true belongs_to :second, class_name: 'Word', optional: true @@ -8,27 +10,22 @@ class Pair < ApplicationRecord validate :check_chat_and_bank def check_chat_and_bank - return false if id.present? == data_bank_id.present? + # Only chat or databank allowed + return unless chat.present? == data_bank.present? + + errors.add(:chat, :present) + errors.add(:data_bank, :present) end class << self - def generate_story(chat) - 10.times.collect { build_sentence(chat, chat.context) }.uniq.join(' ') - end - - def generate(chat:, words:) - words_ids = Word.to_ids(words) - build_sentence(chat, words_ids) || build_sentence(chat, chat.context) - end - - def learn(chat_id: nil, data_bank_id: nil, words:) - raise 'Chat OR Databank must be specified' if chat_id.nil? == data_bank_id.nil? + def learn(chat: nil, data_bank: nil, words:) + raise 'Chat OR Databank must be specified' if chat.nil? == data_bank.nil? Word.learn(words) words_array = [nil] words.each do |word| words_array << word - words_array << nil if Rails.application.secrets.punctuation[:end_sentense].include? word.chars.last + words_array << nil if Rails.application.secrets.end_sentence.include? word.chars.last end words_array << nil unless words_array.last.nil? @@ -36,27 +33,28 @@ def learn(chat_id: nil, data_bank_id: nil, words:) while words_ids.any? trigram = words_ids.take 3 if trigram.first.nil? && trigram.third.nil? && trigram.size > 2 - words_ids.shift(3) + words_ids.shift 3 next end words_ids.shift - pair_params = { chat_id: chat_id, - data_bank_id: data_bank_id, + pair_params = { chat: chat, + data_bank: data_bank, first_id: trigram.first, second_id: trigram.second } pair = Pair.includes(:replies).find_or_create_by!(pair_params) - reply = pair.replies.find_or_create_by(word_id: trigram.third).increment! :count + pair.replies.find_or_create_by(word_id: trigram.third).increment! :count end end - private + def build_sentence(chat: self.chat, words: nil, words_ids: nil) + raise 'words or words_ids must be specified' if words.present? == words_ids.present? - def build_sentence(chat, words_ids) + words_ids = Word.to_ids(words) if words.present? sentence = [] safety_counter = 50 pair_params = { chat: chat, first_id: nil, second_id: words_ids } - while (pair = fetch(pair_params)) && (sentence.size < safety_counter) do + while (pair = fetch_pair(pair_params)) && (sentence.size < safety_counter) do replies = pair.replies.order(count: :desc) replies_pool = 3 + replies.size / 2 reply = replies.limit(replies_pool).sample @@ -67,18 +65,23 @@ def build_sentence(chat, words_ids) Word.to_words(sentence).join(' ').capitalize end - def fetch(chat:, first_id:, second_id:) - if chat.data_bank_ids.any? - chat_ids = [chat.id, nil] - databank_ids = [chat.data_bank_ids, nil].flatten + private + + def fetch_pair(options) + if options[:chat].data_bank_ids.any? + chat_ids = [options[:chat].id, nil] + databank_ids = [options[:chat].data_bank_ids, nil].flatten else - chat_ids = chat.id - data_bank_ids = nil + chat_ids = options[:chat].id + databank_ids = nil end - Pair.includes(:replies).where(chat_id: chat_ids, - data_bank_id: databank_ids, - first_id: first_id, - second_id: second_id).sample + + Pair.includes(:replies) + .order(Arel.sql('RANDOM()')) + .where(chat_id: chat_ids, + data_bank_id: databank_ids, + first_id: options[:first_id], + second_id: options[:second_id]).first end end end diff --git a/app/models/participation.rb b/app/models/participation.rb index e8299ec..7e0e2a7 100644 --- a/app/models/participation.rb +++ b/app/models/participation.rb @@ -1,4 +1,34 @@ +# frozen_string_literal: true + class Participation < ApplicationRecord belongs_to :chat - belongs_to :participant, class_name: 'Chat' + belongs_to :user + + scope :scored, -> { where.not(score: 0) } + + def learn(message) + self.experience += rand(3) if active_at.nil? || 5.minutes.ago > active_at + self.active_at = Time.current + self.left = true if message.left_chat_member&.id == user.id + save! + self + end + + # def level + # experience / asdasdad + # end + + class << self + # def experience_from_level(level) + # (level * (level - 1) / 2.to_f) * 100 + # end + + def learn(message) + chat = Chat.find_by(telegram_id: message&.chat&.id) + user = User.find_by(id: message&.from&.id) + return if chat.nil? || user.nil? + + Participation.find_or_create_by(chat: chat, user: user).learn(message) + end + end end diff --git a/app/models/reply.rb b/app/models/reply.rb index 6fb515c..fea76ce 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,4 +1,6 @@ -class Reply < ActiveRecord::Base +# frozen_string_literal: true + +class Reply < ApplicationRecord belongs_to :pair belongs_to :word, optional: true diff --git a/app/models/single.rb b/app/models/single.rb deleted file mode 100644 index c732817..0000000 --- a/app/models/single.rb +++ /dev/null @@ -1,48 +0,0 @@ -class Single < ApplicationRecord - belongs_to :word - belongs_to :chat - - enum reply_type: %i[sticker document text] - - class << self - def answer(chat_id, word) - word = Word.find_by(word: word) - return nil unless word.present? - word.singles.where(chat_id: chat_id).order(count: :desc).sample - end - - def learn(chat_id, payload) - key = payload.reply_to_message&.text&.split(' ') - answer = [payload.text, payload.sticker, payload.document] - return nil unless key.present? && key.size == 1 && answer.any? - key = key.first.downcase - Word.learn([key]) - if payload.text.present? - text = remove_entities(payload) - reply = { type: :text, data: text } if text.present? - end - reply = { type: :document, data: payload.document.file_id } if payload.document.present? - reply = { type: :sticker, data: payload.sticker.file_id } if payload.sticker.present? - if reply.present? - Single.find_or_create_by(word_id: Word.find_by(word: key).id, - chat_id: chat_id, - reply_type: reply[:type], - reply: reply[:data]).increment! :count - end - end - - private - - def remove_entities(message) - text = message.text.downcase.dup - message.entities.each do |entity| - if entity.offset + entity.length > text.size - NewRelic::Agent.notice_error('PayloadShort', custom_params: { message: message }) - next - end - text[entity.offset, entity.length] = ' ' * entity.length unless entity.type == 'bold' - end - text.split(' ').join(' ') - end - end -end diff --git a/app/models/url.rb b/app/models/url.rb deleted file mode 100644 index b33c48e..0000000 --- a/app/models/url.rb +++ /dev/null @@ -1,7 +0,0 @@ -class Url < ApplicationRecord - def self.seen?(url) - return true if Url.exists? url: url - Url.create url: url - false - end -end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..8c96377 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class User < ApplicationRecord + has_many :participations, dependent: :destroy + has_many :chats, through: :participations + + def to_s + username || first_name || last_name + end + + def to_link + "#{self}" + end + + def self.learn(message) + user = User.find_by(id: message.from.id) || User.new(id: message.from.id) + user.update(message.from.to_h.slice(:username, :first_name, :last_name)) + user + end +end diff --git a/app/models/winner.rb b/app/models/winner.rb index 54c66ac..eb3b69a 100644 --- a/app/models/winner.rb +++ b/app/models/winner.rb @@ -1,65 +1,10 @@ +# frozen_string_literal: true + class Winner < ApplicationRecord belongs_to :chat + belongs_to :user - class << self - def update_stats(chat_id, user_id, count) - chat_id = chat_id.to_s - user_id = user_id.to_s - stats = current_stats(chat_id) - stats = { user_id => 0 } unless stats.present? - stats[user_id] = 0 unless stats[user_id].present? - stats[user_id] += count.to_i - save_stats(chat_id, stats) - end - - def current_stats(chat_id) - JSON.parse(Shizoid::Redis.connection.get(redis_path(chat_id)) || '{}') - end - - def stats(chat_id, user_id = nil) - clean - if user_id.present? - { - dates: Winner.where(chat_id: chat_id, user_id: user_id).order(created_at: :desc).limit(10).pluck(:created_at), - count: Winner.where(chat_id: chat_id, user_id: user_id).count - } - else - Winner.where(chat_id: chat_id).group(:user_id).order(count: :desc).limit(10).count(:user_id) - end - end - - def gambled?(chat_id) - Winner.exists? chat_id: chat_id, created_at: Date.today - end - - def gamble(chat_id) - clean - winner = Winner.find_by(chat_id: chat_id, created_at: Date.today) - return winner.user_id if winner.present? - stats = current_stats(chat_id) - return nil unless stats.present? - winner_id = stats.sort_by { |_, value| value }.reverse[0..2].sample.first - Winner.create(chat_id: chat_id, user_id: winner_id, created_at: Date.today) - drop_stats(chat_id) - winner_id.to_i - end - - private - - def redis_path(chat_id) - "winner_stats_#{chat_id}" - end - - def clean - Winner.where('created_at < ?', 365.days.ago).delete_all - end - - def drop_stats(chat_id) - Shizoid::Redis.connection.del(redis_path(chat_id)) - end - - def save_stats(chat_id, stats) - Shizoid::Redis.connection.set(redis_path(chat_id), stats.to_json) - end + before_save do + self.date ||= Time.zone.today end end diff --git a/app/models/word.rb b/app/models/word.rb index 828fa73..457b1a5 100644 --- a/app/models/word.rb +++ b/app/models/word.rb @@ -1,21 +1,26 @@ -class Word < ApplicationRecord - has_many :singles +# frozen_string_literal: true +class Word < ApplicationRecord class << self def to_words(ids) - words_relations = Word.where(id: ids).pluck(:id, :word).to_h + words_relations = Word.where(id: ids.uniq).pluck(:id, :word).to_h ids.map { |id| words_relations[id] } end def to_ids(words) - words_relations = Word.where(word: words).pluck(:word, :id).to_h + words_relations = Word.where(word: words.uniq).pluck(:word, :id).to_h words.map { |word| words_relations[word] } end def learn(words) - return nil unless words&.compact.present? + words = words&.uniq&.compact + return nil if words.blank? + new_words = words - Word.where(word: words).pluck(:word) - Word.create(new_words.uniq.map { |word| { word: word } }) + return nil if new_words.blank? + + words_array = new_words.uniq.map { |word| { word: word} } + Word.insert_all(words_array) end end end diff --git a/app/processors/concerns/command_parameters.rb b/app/processors/concerns/command_parameters.rb new file mode 100644 index 0000000..26e14ef --- /dev/null +++ b/app/processors/concerns/command_parameters.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module CommandParameters + private + + def command_parameters + @command_parameters ||= text_without_command.downcase + end + + def first_parameter + @first_parameter ||= command_parameters.split(' ').first + end + + def second_parameter + @second_parameter ||= command_parameters.split[1..].join(' ') + end +end diff --git a/app/processors/concerns/processor.rb b/app/processors/concerns/processor.rb new file mode 100644 index 0000000..5100800 --- /dev/null +++ b/app/processors/concerns/processor.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module Processor + attr_reader :message + + delegate :text, :entities, to: :message + + def initialize(message) + @message = message + background_task if respond_to?(:background_task, true) + end + + def responds? + Rails.application.secrets[:allow_all].present? || chat.enabled? + end + + def process + process! if responds? + end + + private + + def process! + raise 'not implemented' + end + + def text_words + text&.split + end + + def text? + text.present? + end + + def ok + I18n.t('.ok').sample + end + + def nok + I18n.t('.nok').sample + end + + def text_without_command + return text unless command? + + text_words[1..].join(' ') + end + + def command? + text&.chars&.first == '/' + end + + def command + return nil unless command? + + text_words.first.split('@').first[1..] + end + + def message_from_bot_owner? + Rails.application.secrets[:telegram][:owners].include? message.from.id + end + + def chat + @chat ||= Chat.find_by! telegram_id: message.chat.id + end + + def user + @user ||= User.find message.from.id + end + + def participation + @participation ||= Participation.find_by!(chat: chat, user: user) + end +end diff --git a/app/processors/message_processor.rb b/app/processors/message_processor.rb new file mode 100644 index 0000000..48d1bc9 --- /dev/null +++ b/app/processors/message_processor.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module MessageProcessor + AVAILABLE_PROCESSORS = %w[Me + Say + BinaryDice + CovidStats + CoolStory + Databank + Eightball + Gab + Help + Holiday + Ids + Ping + Start + Status + Stop + Winner + Text].freeze + def self.call(message) + deserialized_message = Telegram::Bot::Types::Message.new(message) + process_result = nil + AVAILABLE_PROCESSORS.each do |processor| + klass = "MessageProcessor::#{processor}".constantize.new(deserialized_message) + process_result = klass.process if process_result.nil? + end + return if process_result.blank? + + process_result.each { |command, parameters| SendPayloadWorker.perform_async(command, parameters) } + end +end diff --git a/app/processors/message_processor/binary_dice.rb b/app/processors/message_processor/binary_dice.rb new file mode 100644 index 0000000..1ce2901 --- /dev/null +++ b/app/processors/message_processor/binary_dice.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module MessageProcessor + class BinaryDice + include Processor + + def responds? + super && chat.answer_randomly? && achors? + end + + def process! + { send_message: { chat_id: chat.telegram_id, + reply_to_message_id: message.message_id, + text: I18n.t('binary_dice.answers').sample } } + end + + private + + def achors? + text? && text&.chars&.last == '?' && text_words.size > 3 && (text_words & I18n.t('binary_dice.anchors')).any? + end + end +end diff --git a/app/processors/message_processor/cool_story.rb b/app/processors/message_processor/cool_story.rb new file mode 100644 index 0000000..8494751 --- /dev/null +++ b/app/processors/message_processor/cool_story.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module MessageProcessor + class CoolStory + include Processor + + def responds? + super && command == 'cool_story' + end + + def process! + { send_message: { chat_id: chat.telegram_id, + reply_to_message_id: message.message_id, + text: response } } + end + + private + + def response + if chat.answer_randomly?(50) + chat.generate_story + else + I18n.t('cool_story.lazy').sample + end + end + end +end diff --git a/app/processors/message_processor/covid_stats.rb b/app/processors/message_processor/covid_stats.rb new file mode 100644 index 0000000..2c22500 --- /dev/null +++ b/app/processors/message_processor/covid_stats.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module MessageProcessor + class CovidStats + include Processor + include CommandParameters + + def responds? + super && command == 'covid_stats' + end + + def process! + response = case first_parameter + when 'enable', 'on' + enable + when 'disable', 'off' + disable + when 'available' + available_regions + else + show + end + + { send_message: { chat_id: chat.telegram_id, + reply_to_message_id: message.message_id, + parse_mode: :html, + text: response } } + end + + private + + def available_regions + list = I18n.t('covid_stat.regions').map do |key, name| + I18n.t('covid_stat.region_item_html', key: key.to_s.rjust(4), name: name) + end + I18n.t('covid_stat.region_list_html', items: list.join("\n")) + end + + def show + region = (first_parameter || chat.covid_region || 'ALL').to_s.upcase + return nok unless region.in? CovidStat.regions.keys + + CovidStat.day_stats(region: region, return_i18n: true) + end + + def enable + param = second_parameter.to_s.upcase + param = 'ALL' if param.blank? + return nok unless param.in? CovidStat.regions.keys + + chat.update!(covid_region: param) + ok + end + + def disable + chat.update!(covid_region: nil) + ok + end + end +end diff --git a/app/processors/message_processor/databank.rb b/app/processors/message_processor/databank.rb new file mode 100644 index 0000000..5958bfe --- /dev/null +++ b/app/processors/message_processor/databank.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +module MessageProcessor + class Databank + include Processor + include CommandParameters + + def responds? + super && command == 'databank' + end + + def process! + response = case first_parameter + when 'enable', 'on' + enable + when 'disable', 'off' + disable + else + show + end + + { send_message: { chat_id: chat.telegram_id, + text: response, + parse_mode: :html, + reply_to_message_id: message.message_id } } + end + + private + + def enable + databank_id = second_parameter.to_i + return nok unless DataBank.exists?(id: databank_id) && !databank_id.in?(chat.data_bank_ids) + + chat.data_bank_ids << databank_id + chat.save + ok + end + + def disable + if second_parameter.blank? + chat.update!(data_bank_ids: []) + return ok + end + + data_bank_id = second_parameter.to_i + return nok unless data_bank_id.in?(chat.data_bank_ids) + + chat.data_bank_ids -= [data_bank_id] + chat.save! + ok + end + + def show + databanks = DataBank.pluck(:id, :name).to_h + list = databanks.map { |id, name| I18n.t('.databank.list_line_html', id: id, name: name) }.join("\n") + active = chat.data_bank_ids.present? ? chat.data_bank_ids.to_sentence : I18n.t('false') + I18n.t('.databank.list_html', list: list, active: active) + end + end +end diff --git a/app/processors/message_processor/eightball.rb b/app/processors/message_processor/eightball.rb new file mode 100644 index 0000000..70fab8f --- /dev/null +++ b/app/processors/message_processor/eightball.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +module MessageProcessor + class Eightball + include Processor + include CommandParameters + + def responds? + super && (command == 'eightball' || autoreact?) + end + + def process! + response = case command_parameters + when 'enable', 'on' + enable + when 'disable', 'off' + disable + else + generate_prediction + end + + { send_message: { chat_id: chat.telegram_id, reply_to_message_id: message.message_id, text: response } } + end + + private + + def autoreact? + text? && text.chars.last == '?' && chat.eightball? && chat.answer_randomly? + end + + def generate_prediction + return I18n.t('eightball.empty').sample if command_parameters.blank? + + answers = I18n.t('.eightball.replies') + digest = Digest::SHA1.hexdigest(command_parameters).to_i(16) - Date.today.to_time.to_i.div(100) - user.id + answer_id = digest.divmod(answers.count)[1] + answers[answer_id] + end + + def enable + chat.update(eightball: true) + ok + end + + def disable + chat.update(eightball: false) + ok + end + end +end diff --git a/app/processors/message_processor/gab.rb b/app/processors/message_processor/gab.rb new file mode 100644 index 0000000..a4323aa --- /dev/null +++ b/app/processors/message_processor/gab.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module MessageProcessor + class Gab + include Processor + + def responds? + super && command == 'gab' + end + + def process! + response = if text_without_command.present? + set_gab + else + level_response chat.random + end + + { send_message: { chat_id: chat.telegram_id, + parse_mode: :markdown, + text: response, + reply_to_message_id: message.message_id } } + end + + private + + def level_response(value) + I18n.t('.gab.level', chance: value) + end + + def set_gab + value = text_without_command.to_i + if value.in?(0..50) + chat.update(random: value) + level_response value + else + I18n.t('.gab.error') + end + end + end +end diff --git a/app/processors/message_processor/help.rb b/app/processors/message_processor/help.rb new file mode 100644 index 0000000..cb1a90f --- /dev/null +++ b/app/processors/message_processor/help.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module MessageProcessor + class Help + include Processor + + def responds? + super && command == 'help' + end + + def process! + { send_message: { chat_id: chat.telegram_id, + text: I18n.t('.help'), + parse_mode: :markdown, + reply_to_message_id: message.message_id } } + end + end +end diff --git a/app/processors/message_processor/holiday.rb b/app/processors/message_processor/holiday.rb new file mode 100644 index 0000000..a65594a --- /dev/null +++ b/app/processors/message_processor/holiday.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module MessageProcessor + class Holiday + include Processor + + def responds? + false + # super && command == 'holiday' + end + + def process! + # TODO + response = 'holiday response' + { send_message: { chat_id: chat.telegram_id, text: response, reply_to_message_id: message.message_id } } + end + end +end diff --git a/app/processors/message_processor/ids.rb b/app/processors/message_processor/ids.rb new file mode 100644 index 0000000..953450c --- /dev/null +++ b/app/processors/message_processor/ids.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module MessageProcessor + class Ids + include Processor + + def responds? + command == 'ids' + end + + def process! + { send_message: { chat_id: chat.telegram_id, + text: response, + reply_to_message_id: message.message_id, + parse_mode: :markdown } } + end + + private + + def response + I18n.t('.ids', type: chat.kind, + chat: chat.telegram_id, + user: user.id) + end + end +end diff --git a/app/processors/message_processor/me.rb b/app/processors/message_processor/me.rb new file mode 100644 index 0000000..119505b --- /dev/null +++ b/app/processors/message_processor/me.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module MessageProcessor + class Me + include Processor + + def responds? + super && command == 'me' + end + + def process! + { delete_message: { chat_id: chat.telegram_id, message_id: message.message_id }, + send_message: payload } + end + + def payload + result = { chat_id: chat.telegram_id, parse_mode: :html, text: reaction } + result[:reply_to_message_id] = message.reply_to_message.message_id if message.reply_to_message.present? + + result + end + + def reaction + return "#{user.to_link} #{text_without_command}" if text_without_command.present? + + "#{user.to_link} #{I18n.t('.me').sample}" + end + end +end diff --git a/app/processors/message_processor/ping.rb b/app/processors/message_processor/ping.rb new file mode 100644 index 0000000..6c9b899 --- /dev/null +++ b/app/processors/message_processor/ping.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module MessageProcessor + class Ping + include Processor + + def responds? + super && command == 'ping' + end + + def process! + { send_message: { chat_id: chat.telegram_id, + reply_to_message_id: message.message_id, + text: I18n.t('.ping').sample } } + end + end +end diff --git a/app/processors/message_processor/say.rb b/app/processors/message_processor/say.rb new file mode 100644 index 0000000..49db0ef --- /dev/null +++ b/app/processors/message_processor/say.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module MessageProcessor + class Say + include Processor + + def responds? + super && command == 'say' && message_from_bot_owner? + end + + def process! + { delete_message: { chat_id: chat.telegram_id, message_id: message.message_id }, + send_message: payload } + end + + def payload + result = { chat_id: chat.telegram_id, text: text_without_command } + result[:reply_to_message_id] = message.reply_to_message.message_id if message.reply_to_message.present? + result + end + end +end diff --git a/app/processors/message_processor/start.rb b/app/processors/message_processor/start.rb new file mode 100644 index 0000000..031b3b8 --- /dev/null +++ b/app/processors/message_processor/start.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module MessageProcessor + class Start + include Processor + + def responds? + command == 'start' && message_from_bot_owner? + end + + def process! + chat.enable! + { send_message: { chat_id: chat.telegram_id, reply_to_message_id: message.message_id, text: ok } } + end + end +end diff --git a/app/processors/message_processor/status.rb b/app/processors/message_processor/status.rb new file mode 100644 index 0000000..16b3c0a --- /dev/null +++ b/app/processors/message_processor/status.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module MessageProcessor + class Status + include Processor + + def responds? + super && command == 'status' + end + + def process! + { send_message: { chat_id: chat.telegram_id, + parse_mode: :markdown, + reply_to_message_id: message.message_id, + text: response } } + end + + private + + def response + I18n.t('.status', version: Rails.configuration.secrets[:release], + active: I18n.t(chat.enabled?.to_s), + gab: chat.random, + pairs: chat.pairs.size, + message_count: message.message_id, + databanks: I18n.t(chat.data_bank_ids.present?.to_s), + auto_eightball: I18n.t(chat.eightball?.to_s), + winner: chat.winner || I18n.t('.winner.disabled')) + end + end +end diff --git a/app/processors/message_processor/stop.rb b/app/processors/message_processor/stop.rb new file mode 100644 index 0000000..4aa1cf5 --- /dev/null +++ b/app/processors/message_processor/stop.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module MessageProcessor + class Stop + include Processor + + def responds? + command == 'stop' && message_from_bot_owner? + end + + def process! + chat.disable! + { send_message: { chat_id: chat.telegram_id, reply_to_message_id: message.message_id, text: ok }, + leave_chat: { chat_id: chat.telegram_id } } + end + end +end diff --git a/app/processors/message_processor/text.rb b/app/processors/message_processor/text.rb new file mode 100644 index 0000000..51f2bb1 --- /dev/null +++ b/app/processors/message_processor/text.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module MessageProcessor + class Text + include Processor + + def responds? + super && + text? && + !command? && + respond_to_message? && + response_text.present? + end + + def process! + { send_message: { chat_id: chat.telegram_id, + text: response_text, + reply_to_message_id: message.message_id } } + end + + private + + def respond_to_message? + message&.reply_to_message&.from&.id == TelegramService.bot_id || + anchors? || + chat.answer_randomly? || + chat.kind == 'private' + end + + def response_text + @response_text ||= chat.generate_reply(text.split) + end + + def anchors? + (text_words & I18n.t('.text.anchors')).any? + end + + def background_task + return unless text? && !command? && chat.enabled? + + PairUpdateWorker.perform_async(chat.id, text) + + chat.context Word.to_ids(text_words) if text_words&.any? + end + end +end diff --git a/app/processors/message_processor/winner.rb b/app/processors/message_processor/winner.rb new file mode 100644 index 0000000..6e6bfb4 --- /dev/null +++ b/app/processors/message_processor/winner.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +module MessageProcessor + class Winner + include Processor + include CommandParameters + + def responds? + super && command == 'winner' + end + + def process! + response = case first_parameter + when 'enable', 'on' + enable + when 'disable', 'off' + disable + when 'current' + current_stats + else + previous_winner + end + + { send_message: { chat_id: chat.telegram_id, + reply_to_message_id: message.message_id, + parse_mode: :html, + text: response } } + end + + private + + def current_stats + top = chat.participations + .includes(:user) + .order(score: :desc) + .limit(10) + .each_with_index + .map do |p, i| + I18n.t('winner.top_line_html', position: i + 1, user: p.user.to_s, score: p.score) if p.user.present? + end.compact.join("\n") + + I18n.t('winner.current_html', top: top) + end + + def previous_winner + winner = chat.winners.order(created_at: :desc)&.first&.user&.to_s + + if winner.present? + top = chat.winners + .where('date > ?', 1.year.ago) + .group(:user) + .order(count: :desc) + .limit(10) + .count(:user) + .each_with_index + .map { |(user, wins), idx| I18n.t('winner.top_line_html', position: idx + 1, user: user.to_s, score: wins) } + .join("\n") + + I18n.t('winner.winner_html', top: top, name: chat.winner, user: winner) + else + I18n.t('winner.no_one') + end + end + + def background_task + return if command? || chat.disabled? || chat.winner_disabled? + + new_score = text_words&.size || 1 + participation.update!(score: participation.score + new_score) + end + + def enable + return nok if second_parameter.blank? + + chat.update!(winner: second_parameter) + ok + end + + def disable + chat.update!(winner: nil) + ok + end + end +end diff --git a/app/processors/update_processor.rb b/app/processors/update_processor.rb new file mode 100644 index 0000000..648eba3 --- /dev/null +++ b/app/processors/update_processor.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class UpdateProcessor + def self.call(update) + message = update['message'] + return if message.blank? + + data = Telegram::Bot::Types::Message.new message + chat = Chat.learn(data) + User.learn(data) + Participation.learn(data) + + queue_name = "chat_#{chat.id}" + Sidekiq::Queue[queue_name].limit = 1 + MessageWorker.set(queue: queue_name).perform_async(message) + end +end diff --git a/app/services/redis_service.rb b/app/services/redis_service.rb new file mode 100644 index 0000000..790e110 --- /dev/null +++ b/app/services/redis_service.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module RedisService + class << self + def connection + @connection ||= Redis.new(Rails.configuration.secrets[:cache_redis]) + end + + delegate :multi, :lrange, :set, :get, :incr, :decr, to: :connection + end +end diff --git a/app/services/stop_covid_service.rb b/app/services/stop_covid_service.rb new file mode 100644 index 0000000..00acff0 --- /dev/null +++ b/app/services/stop_covid_service.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module StopCovidService + class << self + ALLOWED_PARAMS = %i[date sick healed died first second].freeze + + def get_data(region_id) + body = Faraday.get(url(region_id)).body + json = JSON.parse(body).map(&:symbolize_keys) + json.map do |record| + new_date = Date.parse(record[:date]) + record.slice(*ALLOWED_PARAMS).except(:date).merge(date: new_date) + end + end + + private + + def url(region_id) + "#{Rails.configuration.secrets[:stop_covid_url]}/covid_data.json?do=region_stats&code=RU-#{region_id.upcase}" + end + end +end diff --git a/app/services/telegram_service.rb b/app/services/telegram_service.rb new file mode 100644 index 0000000..0687494 --- /dev/null +++ b/app/services/telegram_service.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +module TelegramService + class << self + def bot_api + return @bot_api if @bot_api.present? + + @bot_api = Telegram::Bot::Api.new(token) + + if webhook.present? + @bot_api.set_webhook(url: "#{webhook}/#{token_secret}") + else + @bot_api.delete_webhook + end + @bot_api + end + + def webhook + Rails.configuration.secrets[:telegram][:webhook] + end + + def token_secret + @token_secret ||= token.split(':').last + end + + def bot_id + @bot_id ||= token.split(':').first.to_i + end + + def token + telegram_token = Rails.configuration.secrets[:telegram][:token] + raise 'Telegram token is not configured' if telegram_token.blank? + + telegram_token + end + + def start_polling + bot_api + Telegram::Bot::Client.run(token) do |bot| + bot.listen { |update| UpdateWorker.perform_async(update) } + end + end + + delegate :send_message, :leave_chat, :delete_message, to: :bot_api + end +end diff --git a/app/workers/chat_destroyer.rb b/app/workers/chat_destroyer.rb deleted file mode 100644 index 7f5c1b1..0000000 --- a/app/workers/chat_destroyer.rb +++ /dev/null @@ -1,9 +0,0 @@ -class ChatDestroyer - include Sidekiq::Worker - sidekiq_options queue: 'deleting' - - def perform(id) - chat = Chat.find_by(id: id) - chat.destroy if chat.present? && !chat.personal? - end -end diff --git a/app/workers/chat_updater.rb b/app/workers/chat_updater.rb deleted file mode 100644 index 8f28e36..0000000 --- a/app/workers/chat_updater.rb +++ /dev/null @@ -1,15 +0,0 @@ -class ChatUpdater - include Sidekiq::Worker - sidekiq_options queue: 'updating' - - def perform(options) - chat = Chat.find_by(id: options['id']) - return if chat.nil? - meta = { title: options['title'], - first_name: options['first_name'], - last_name: options['last_name'], - username: options['username'], - kind: options['kind'] } - chat.update_meta(meta) - end -end diff --git a/app/workers/context_updater.rb b/app/workers/context_updater.rb deleted file mode 100644 index 7e5c9ce..0000000 --- a/app/workers/context_updater.rb +++ /dev/null @@ -1,9 +0,0 @@ -class ContextUpdater - include Sidekiq::Worker - sidekiq_options queue: 'updating' - - def perform(options) - chat = Chat.find_by(id: options['id']) - chat.context Word.to_ids(options['words']) unless chat.nil? - end -end diff --git a/app/workers/covid_region_updater_worker.rb b/app/workers/covid_region_updater_worker.rb new file mode 100644 index 0000000..a74664c --- /dev/null +++ b/app/workers/covid_region_updater_worker.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CovidRegionUpdaterWorker + include Sidekiq::Worker + + sidekiq_options queue: 'updating' + sidekiq_options retry: false + + def perform(region_id) + CovidStat.update(region_id) + end +end diff --git a/app/workers/covid_sender_worker.rb b/app/workers/covid_sender_worker.rb new file mode 100644 index 0000000..51b86ff --- /dev/null +++ b/app/workers/covid_sender_worker.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class CovidSenderWorker + include Sidekiq::Worker + + sidekiq_options retry: false + + def perform + dates = CovidStat.group(:region).maximum(:date) + dates['ALL'] = dates.values.max + Chat.active.where.not(covid_region: nil).each do |c| + next if c.covid_last_notification.present? && c.covid_last_notification >= dates[c.covid_region] + + c.update!(covid_last_notification: dates[c.covid_region]) + parameters = { text: CovidStat.day_stats(region: c.covid_region, return_i18n: true), + parse_mode: :html, + chat_id: c.telegram_id } + SendPayloadWorker.perform_async(:send_message, parameters) + end + end +end diff --git a/app/workers/covid_updater_worker.rb b/app/workers/covid_updater_worker.rb new file mode 100644 index 0000000..5e14100 --- /dev/null +++ b/app/workers/covid_updater_worker.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CovidUpdaterWorker + include Sidekiq::Worker + + sidekiq_options queue: 'updating' + sidekiq_options retry: false + + def perform + CovidStat.update_all if CovidStat.needs_update? + end +end diff --git a/app/workers/idle_check_worker.rb b/app/workers/idle_check_worker.rb new file mode 100644 index 0000000..7a89bf4 --- /dev/null +++ b/app/workers/idle_check_worker.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class IdleCheckWorker + include Sidekiq::Worker + sidekiq_options retry: false + + def perform + Chat.active.where('active_at > ?', 7.days.ago).each do |c| + c.update!(active_at: Time.current) + SendPayloadWorker.perform_async(:send_message, + text: I18n.t('idle').sample, + chat_id: c.telegram_id) + end + end +end diff --git a/app/workers/message_worker.rb b/app/workers/message_worker.rb new file mode 100644 index 0000000..b293c26 --- /dev/null +++ b/app/workers/message_worker.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class MessageWorker + include Sidekiq::Worker + sidekiq_options retry: false + + def perform(message) + MessageProcessor.call message + end +end diff --git a/app/workers/pair_update_worker.rb b/app/workers/pair_update_worker.rb new file mode 100644 index 0000000..fbba695 --- /dev/null +++ b/app/workers/pair_update_worker.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class PairUpdateWorker + include Sidekiq::Worker + sidekiq_options queue: 'learning', retry: false + + def perform(chat_id, text) + chat = Chat.find(chat_id) + + Pair.learn(chat: chat, words: text.split) + end +end diff --git a/app/workers/pair_updater.rb b/app/workers/pair_updater.rb deleted file mode 100644 index f527eab..0000000 --- a/app/workers/pair_updater.rb +++ /dev/null @@ -1,9 +0,0 @@ -class PairUpdater - include Sidekiq::Worker - sidekiq_options queue: 'learning' - - def perform(options) - chat = Chat.find_by(id: options['id']) - Pair.learn(chat_id: chat.id, words: options['words']) - end -end diff --git a/app/workers/participant_updater.rb b/app/workers/participant_updater.rb deleted file mode 100644 index 99a3226..0000000 --- a/app/workers/participant_updater.rb +++ /dev/null @@ -1,13 +0,0 @@ -class ParticipantUpdater - include Sidekiq::Worker - sidekiq_options queue: 'updating' - - def perform(options) - chat = Chat.find_by(telegram_id: options['chat_id']) - user = Chat.find_by(telegram_id: options['user_id']) - return if chat.nil? || user.nil? || options['chat_id'] == options['user_id'] - participation = chat.participations.find_or_create_by(participant_id: user.id) - participation.present = options['user_id'] != options['left_id'] - participation.save - end -end diff --git a/app/workers/send_payload_worker.rb b/app/workers/send_payload_worker.rb new file mode 100644 index 0000000..b71ef2e --- /dev/null +++ b/app/workers/send_payload_worker.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class SendPayloadWorker + include Sidekiq::Worker + sidekiq_options retry: false + + def perform(command, parameters) + TelegramService.send(command.to_sym, parameters) + end +end diff --git a/app/workers/stats_updater.rb b/app/workers/stats_updater.rb deleted file mode 100644 index 742949f..0000000 --- a/app/workers/stats_updater.rb +++ /dev/null @@ -1,9 +0,0 @@ -class StatsUpdater - include Sidekiq::Worker - sidekiq_options queue: 'updating' - - def perform(options) - chat = Chat.find_by(id: options['id']) - Winner.update_stats(chat.id, options['from'], options['count']) - end -end diff --git a/app/workers/update_worker.rb b/app/workers/update_worker.rb new file mode 100644 index 0000000..dc37038 --- /dev/null +++ b/app/workers/update_worker.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class UpdateWorker + include Sidekiq::Worker + sidekiq_options queue: :default, retry: false + + def perform(update) + UpdateProcessor.call update + end +end diff --git a/app/workers/winner_check_worker.rb b/app/workers/winner_check_worker.rb new file mode 100644 index 0000000..3bed7b4 --- /dev/null +++ b/app/workers/winner_check_worker.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class WinnerCheckWorker + include Sidekiq::Worker + + def perform + Chat.active.with_winners.reject(&:current_winner).each { |c| WinnerGambleWorker.perform_async(c.id) } + end +end diff --git a/app/workers/winner_gamble_worker.rb b/app/workers/winner_gamble_worker.rb new file mode 100644 index 0000000..50b7a8d --- /dev/null +++ b/app/workers/winner_gamble_worker.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +class WinnerGambleWorker + include Sidekiq::Worker + + def perform(chat_id) + chat = Chat.find(chat_id) + winner = chat.choose_winner! + return if winner.nil? + + top = chat.winners + .where('date > ?', 1.year.ago) + .group(:user) + .order(count: :desc) + .limit(10) + .count(:user) + .each_with_index + .map { |(user, wins), idx| I18n.t('winner.top_line_html', position: idx + 1, user: user.to_s, score: wins) } + .join("\n") + + parameters = { text: I18n.t('winner.winner_html', top: top, name: chat.winner, user: winner.to_link), + parse_mode: :html, + disable_notification: true, + chat_id: chat.telegram_id } + SendPayloadWorker.perform_async(:send_message, parameters) + end +end diff --git a/bin/bundle b/bin/bundle index 66e9889..4724043 100755 --- a/bin/bundle +++ b/bin/bundle @@ -1,3 +1,107 @@ #!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -load Gem.bin_path('bundler', 'bundle') + +require 'rubygems' + +m = Module.new do + module_function + + def invoked_as_script? + File.expand_path($0) == File.expand_path(__FILE__) + end + + def env_var_version + ENV['BUNDLER_VERSION'] + end + + def cli_arg_version + return unless invoked_as_script? # don't want to hijack other binstubs + return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + + bundler_version = nil + update_index = nil + ARGV.each_with_index do |a, i| + if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN + bundler_version = a + end + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ + bundler_version = $1 + update_index = i + end + bundler_version + end + + def gemfile + gemfile = ENV["BUNDLE_GEMFILE"] + return gemfile if gemfile && !gemfile.empty? + + File.expand_path("../../Gemfile", __FILE__) + end + + def lockfile + lockfile = + case File.basename(gemfile) + when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) + else "#{gemfile}.lock" + end + File.expand_path(lockfile) + end + + def lockfile_version + return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + Regexp.last_match(1) + end + + def bundler_version + @bundler_version ||= + env_var_version || cli_arg_version || + lockfile_version + end + + def bundler_requirement + return "#{Gem::Requirement.default}.a" unless bundler_version + + bundler_gem_version = Gem::Version.new(bundler_version) + + requirement = bundler_gem_version.approximate_recommendation + + return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0") + + requirement += ".a" if bundler_gem_version.prerelease? + + requirement + end + + def load_bundler! + ENV["BUNDLE_GEMFILE"] ||= gemfile + + activate_bundler + end + + def activate_bundler + gem_error = activation_error_handling do + gem "bundler", bundler_requirement + end + return if gem_error.nil? + require_error = activation_error_handling do + require "bundler/version" + end + return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" + exit 42 + end + + def activation_error_handling + yield + nil + rescue StandardError, LoadError => e + e + end +end + +m.load_bundler! + +if m.invoked_as_script? + load Gem.bin_path("bundler", "bundle") +end diff --git a/bin/copy_samples b/bin/copy_samples deleted file mode 100755 index abe7f48..0000000 --- a/bin/copy_samples +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env ruby - -require 'fileutils' - -rails_root = File.expand_path('../../', __FILE__) - -%w( - config/database.sample.yml - config/secrets.sample.yml -).each do |file| - source = "#{rails_root}/#{file}" - target = "#{rails_root}/#{file.sub('.sample', '')}" - if File.exist?(target) - puts "#{file}: exists, skipping" - elsif !File.exist?(source) - puts "#{file}: not found" - else - FileUtils.copy(source, target) - puts "#{file}: OK" - end -end diff --git a/bin/rails b/bin/rails index 5badb2f..efc0377 100755 --- a/bin/rails +++ b/bin/rails @@ -1,9 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -APP_PATH = File.expand_path('../config/application', __dir__) -require_relative '../config/boot' -require 'rails/commands' +APP_PATH = File.expand_path("../config/application", __dir__) +require_relative "../config/boot" +require "rails/commands" diff --git a/bin/rake b/bin/rake index d87d5f5..4fbf10b 100755 --- a/bin/rake +++ b/bin/rake @@ -1,9 +1,4 @@ #!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -require_relative '../config/boot' -require 'rake' +require_relative "../config/boot" +require "rake" Rake.application.run diff --git a/bin/rspec b/bin/rspec deleted file mode 100755 index 6e67092..0000000 --- a/bin/rspec +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env ruby -begin - load File.expand_path('../spring', __FILE__) -rescue LoadError => e - raise unless e.message.include?('spring') -end -require 'bundler/setup' -load Gem.bin_path('rspec-core', 'rspec') diff --git a/bin/setup b/bin/setup index 104e40c..ec47b79 100755 --- a/bin/setup +++ b/bin/setup @@ -1,35 +1,33 @@ #!/usr/bin/env ruby -require 'pathname' -require 'fileutils' -include FileUtils +require "fileutils" # path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) +APP_ROOT = File.expand_path("..", __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") end -chdir APP_ROOT do - # This script is a starting point to setup your application. +FileUtils.chdir APP_ROOT do + # This script is a way to set up or update your development environment automatically. + # This script is idempotent, so that you can run it at any time and get an expectable outcome. # Add necessary setup steps to this file. - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - + puts "== Installing dependencies ==" + system! "gem install bundler --conservative" + system("bundle check") || system!("bundle install") # puts "\n== Copying sample files ==" - # unless File.exist?('config/database.yml') - # cp 'config/database.yml.sample', 'config/database.yml' + # unless File.exist?("config/database.yml") + # FileUtils.cp "config/database.yml.sample", "config/database.yml" # end puts "\n== Preparing database ==" - system! 'bin/rails db:setup' + system! "bin/rails db:prepare" puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' + system! "bin/rails log:clear tmp:clear" puts "\n== Restarting application server ==" - system! 'bin/rails restart' + system! "bin/rails restart" end diff --git a/bin/spring b/bin/spring deleted file mode 100755 index fb2ec2e..0000000 --- a/bin/spring +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env ruby - -# This file loads spring without using Bundler, in order to be fast. -# It gets overwritten when you run the `spring binstub` command. - -unless defined?(Spring) - require 'rubygems' - require 'bundler' - - lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) - spring = lockfile.specs.detect { |spec| spec.name == "spring" } - if spring - Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path - gem 'spring', spring.version - require 'spring/binstub' - end -end diff --git a/bin/update b/bin/update deleted file mode 100755 index a8e4462..0000000 --- a/bin/update +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env ruby -require 'pathname' -require 'fileutils' -include FileUtils - -# path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) - -def system!(*args) - system(*args) || abort("\n== Command #{args} failed ==") -end - -chdir APP_ROOT do - # This script is a way to update your development environment automatically. - # Add necessary update steps to this file. - - puts '== Installing dependencies ==' - system! 'gem install bundler --conservative' - system('bundle check') || system!('bundle install') - - puts "\n== Updating database ==" - system! 'bin/rails db:migrate' - - puts "\n== Removing old logs and tempfiles ==" - system! 'bin/rails log:clear tmp:clear' - - puts "\n== Restarting application server ==" - system! 'bin/rails restart' -end diff --git a/compose.sh b/compose.sh new file mode 100755 index 0000000..cb05e01 --- /dev/null +++ b/compose.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +CONTAINER_UID=${UID} CONTAINER_GID=${GID} docker compose $@ diff --git a/config.ru b/config.ru index f7ba0b5..2e03084 100644 --- a/config.ru +++ b/config.ru @@ -1,5 +1,8 @@ +# frozen_string_literal: true + # This file is used by Rack-based servers to start the application. -require_relative 'config/environment' +require_relative "config/environment" run Rails.application +Rails.application.load_server diff --git a/config/application.rb b/config/application.rb index 37fdc76..92468eb 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,23 +1,43 @@ +# frozen_string_literal: true + require_relative 'boot' require 'rails' -# require "active_model/railtie" -require 'active_job/railtie' -require "active_record/railtie" +# Pick the frameworks you want: +require 'active_model/railtie' +# require 'active_job/railtie' +require 'active_record/railtie' +# require 'active_storage/engine' require 'action_controller/railtie' -# require "action_mailer/railtie" -# require "action_view/railtie" -# require "action_cable/engine" -# require "sprockets/railtie" -# require "rails/test_unit/railtie" +# require 'action_mailer/railtie' +# require 'action_mailbox/engine' +# require 'action_text/engine' +require 'action_view/railtie' +# require 'action_cable/engine' +# require 'sprockets/railtie' +require 'rails/test_unit/railtie' +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module Shizoid class Application < Rails::Application - config.load_defaults 5.1 - config.i18n.default_locale = :ru + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 7.0 + + # Configuration for the application, engines, and railties goes here. + # + # These settings can be overridden in specific environments using the files + # in config/environments, which are processed later. + # + # config.time_zone = "Central Time (US & Canada)" + # config.eager_load_paths << Rails.root.join("extras") + + + # Only loads a smaller set of middleware suitable for API only apps. + # Middleware like session, flash, cookies can be added back manually. + # Skip views, helpers and assets when generating a new resource. config.api_only = true - config.active_job.queue_adapter = :sidekiq end end diff --git a/config/boot.rb b/config/boot.rb index 5cea2ab..2ecfc50 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,3 +1,4 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) -require 'bundler/setup' +require "bundler/setup" # Set up gems listed in the Gemfile. +require 'telegram/bot' diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..3078e18 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,20 @@ +default: &default + adapter: postgresql + username: <%= ENV['DATABASE_USERNAME'] %> + password: <%= ENV['DATABASE_PASSWORD'] %> + host: <%= ENV['DATABASE_HOST'] %> + port: <%= ENV['DATABASE_PORT'] %> + pool: <%= ENV.fetch("DATABASE_POOL_SIZE") { 40 } %> + timeout: 5000 + +development: + <<: *default + database: <%= "#{ENV['DATABASE_NAME']}_development" %> + +test: + <<: *default + database: <%= "#{ENV['DATABASE_NAME']}_test" %> + +production: + <<: *default + database: <%= "#{ENV['DATABASE_NAME']}_production" %> diff --git a/config/database.yml.example b/config/database.yml.example deleted file mode 100644 index e9f10da..0000000 --- a/config/database.yml.example +++ /dev/null @@ -1,15 +0,0 @@ -default: &default - adapter: postgresql - pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - timeout: 5000 - -development: - <<: *default - database: shizoid_dev -test: - <<: *default - database: shizoid_test - -production: - <<: *default - database: shizoid diff --git a/config/deploy.rb.example b/config/deploy.rb.example deleted file mode 100644 index 453c245..0000000 --- a/config/deploy.rb.example +++ /dev/null @@ -1,26 +0,0 @@ -set :application, 'shizoid' -set :deploy_user, 'shizoid' -set :server_address, '10.0.0.2' -set :ssh_port, 22 - -set :format, :pretty -set :repo_url, 'git@github.com:top4ek/shizoid.git' -set :rvm_type, :user -set :rvm_ruby_version, '2.5.0' -set :deploy_to, "/home/#{fetch :deploy_user}/#{fetch :application}" -set :log, "#{fetch :deploy_to}/log" -set :keep_releases, 3 - -set :linked_files, %w{config/database.yml config/secrets.yml} -set :linked_dirs, %w{log run tmp} - -set :config_files, %w(shizoid.service sidekiq-shizoid.service nginx) -set(:symlinks, [ - { source: 'shizoid.service', link: "/etc/systemd/system/#{fetch :application}.service" }, - { source: 'nginx.conf', link: "/etc/nginx/conf.d/#{fetch :application}.conf" }, - { source: 'shizoid-sidekiq.service', link: "/etc/systemd/system/sidekiq-#{fetch :application}.service" } -]) - -before 'deploy', 'deploy:generate_config_files' -before 'deploy:setup', 'deploy:generate_config_files' -after 'deploy:finished', 'deploy:restart' diff --git a/config/deploy/Containerfile b/config/deploy/Containerfile new file mode 100644 index 0000000..24566e3 --- /dev/null +++ b/config/deploy/Containerfile @@ -0,0 +1,11 @@ +FROM docker.io/ruby:3.1.1-alpine3.15 +EXPOSE 3000 +WORKDIR /opt/app/src +ENV LANG ru_RU.utf8 + +RUN apk add --no-cache --update git build-base ca-certificates less postgresql-dev postgresql-client tzdata make libffi-dev libxml2 libxml2-dev libxslt-dev && \ + cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime && \ + echo "Europe/Moscow" > /etc/timezone + +ADD Gemfile Gemfile.lock /opt/app/src/ +RUN bundle config without production && bundle install --retry=3 --jobs 20 diff --git a/config/deploy/Containerfile.receiver b/config/deploy/Containerfile.receiver new file mode 100644 index 0000000..4d28caa --- /dev/null +++ b/config/deploy/Containerfile.receiver @@ -0,0 +1,43 @@ +FROM golang:1.17-alpine AS base +WORKDIR /opt/app + +ENV GO111MODULE="on" +ENV GOOS="linux" +ENV CGO_ENABLED=0 + +RUN apk update && \ + apk add --no-cache ca-certificates git && \ + update-ca-certificates + +# Development +FROM base AS dev +WORKDIR /opt/app + +EXPOSE 8080 +# EXPOSE 2345 + +RUN go get -u github.com/cosmtrek/air + +ENTRYPOINT ["/opt/app/_dev_start.sh"] + +# Production build +FROM base AS builder +WORKDIR /app + +COPY . /app + +RUN go mod download && \ + go mod verify && \ + go build -o receiver -a . + +# Production +FROM alpine:latest +EXPOSE 8080 + +RUN apk update && \ + apk add --no-cache ca-certificates curl tzdata && \ + update-ca-certificates + +COPY --from=builder /app/receiver /opt/receiver + +ENTRYPOINT ["/opt/receiver"] diff --git a/config/deploy/Containerfile.tor b/config/deploy/Containerfile.tor new file mode 100644 index 0000000..b646c09 --- /dev/null +++ b/config/deploy/Containerfile.tor @@ -0,0 +1,7 @@ +FROM alpine:latest + +EXPOSE 9150 + +RUN apk --no-cache add tor + +CMD ["tor"] diff --git a/config/deploy/nginx.conf.erb b/config/deploy/nginx.conf.erb deleted file mode 100644 index b2e3fa7..0000000 --- a/config/deploy/nginx.conf.erb +++ /dev/null @@ -1,45 +0,0 @@ -upstream puma { - server unix:/tmp/shizoid.puma.sock fail_timeout=0; -} - -server { - listen 80; - server_name <%= fetch :server_address %>; - return 301 https://$host$request_uri; -} - -server { - listen 443 ssl; - server_name <%= fetch :server_address %>; - - ssl_certificate /etc/letsencrypt/live/<%= fetch :server_address %>/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/<%= fetch :server_address %>/privkey.pem; - - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_prefer_server_ciphers on; - - ssl_dhparam /etc/openvpn/easy-rsa/keys/dh.pem; - ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA'; - ssl_session_timeout 1d; - ssl_session_cache shared:SSL:50m; - ssl_stapling on; - ssl_stapling_verify on; - add_header Strict-Transport-Security max-age=15768000; - root <%= "#{release_path}" %>/public; - location ~ /.well-known { - allow all; - } - location @puma { - proxy_pass http://puma; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Client-IP $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_redirect off; - } - location /telegram { - try_files $uri @puma; - } - location ~ /.well-known { - allow all; - } -} diff --git a/config/deploy/production.rb b/config/deploy/production.rb deleted file mode 100644 index 44f698b..0000000 --- a/config/deploy/production.rb +++ /dev/null @@ -1,2 +0,0 @@ -set :rails_env, 'production' -server "#{fetch :server_address}", user: "#{fetch :deploy_user}", roles: %w{app db web}, port: "#{fetch :ssh_port}" diff --git a/config/deploy/shizoid.service.erb b/config/deploy/shizoid.service.erb deleted file mode 100644 index c328f9d..0000000 --- a/config/deploy/shizoid.service.erb +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description=<%= fetch :application %> -After=syslog.target network.target - -[Service] -Type=simple -Environment=RAILS_ENV=production -WorkingDirectory=<%= "#{release_path}" %> -ExecStart=/home/<%= fetch :deploy_user %>/.rvm/wrappers/ruby-<%= fetch :rvm_ruby_version %>@<%= fetch :application %>/bundle exec rails server -User=<%= fetch :deploy_user %> -Group=<%= fetch :deploy_user %> -UMask=0002 -RestartSec=5 -Restart=on-failure -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=<%= fetch :application %> - -[Install] -WantedBy=multi-user.target diff --git a/config/deploy/shizoid_sidekiq.service.erb b/config/deploy/shizoid_sidekiq.service.erb deleted file mode 100644 index f6bd7e8..0000000 --- a/config/deploy/shizoid_sidekiq.service.erb +++ /dev/null @@ -1,20 +0,0 @@ -[Unit] -Description=<%= fetch :application %> -After=syslog.target network.target - -[Service] -Type=simple -Environment=RAILS_ENV=production -WorkingDirectory=<%= "#{release_path}" %> -ExecStart=/home/<%= fetch :deploy_user %>/.rvm/wrappers/ruby-<%= fetch :rvm_ruby_version %>@<%= fetch :application %>/bundle exec sidekiq -C config/sidekiq.yml -L log/sidekiq.log -User=<%= fetch :deploy_user %> -Group=<%= fetch :deploy_user %> -UMask=0002 -RestartSec=5 -Restart=on-failure -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=<%= fetch :application %> - -[Install] -WantedBy=multi-user.target diff --git a/config/environment.rb b/config/environment.rb index 426333b..cac5315 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,5 @@ # Load the Rails application. -require_relative 'application' +require_relative "application" # Initialize the Rails application. Rails.application.initialize! diff --git a/config/environments/development.rb b/config/environments/development.rb index f1f16df..5bb4432 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,8 +1,10 @@ +require "active_support/core_ext/integer/time" + Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false @@ -12,13 +14,15 @@ # Show full error reports. config.consider_all_requests_local = true - # Enable/disable caching. By default caching is disabled. - if Rails.root.join('tmp/caching-dev.txt').exist? - config.action_controller.perform_caching = true + # Enable server timing + config.server_timing = true + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. + if Rails.root.join("tmp/caching-dev.txt").exist? config.cache_store = :memory_store config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" + "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false @@ -29,14 +33,25 @@ # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + # Raise an error on page load if there are pending migrations. config.active_record.migration_error = :page_load + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. - config.file_watcher = ActiveSupport::EventedFileUpdateChecker + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true end diff --git a/config/environments/production.rb b/config/environments/production.rb index e3cbf0b..98e3fb2 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,7 +1,6 @@ -Rails.application.configure do - # Set application domain, to be able to run `rake telegram:bot:set_webhook` - routes.default_url_options = { host: 'bot.aleschenko.ru', protocol: 'https' } +require "active_support/core_ext/integer/time" +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. # Code is not reloaded between requests. @@ -15,32 +14,28 @@ # Full error reports are disabled and caching is turned on. config.consider_all_requests_local = false - config.action_controller.perform_caching = true - # Attempt to read encrypted secrets from `config/secrets.yml.enc`. - # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or - # `config/secrets.yml.key`. - config.read_encrypted_secrets = true + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. - config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? - + config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' + # config.asset_host = "http://assets.example.com" # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX - + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache + # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true - # Use the lowest log level to ensure availability of diagnostic information - # when problems arise. - config.log_level = :debug + # Include generic and useful information about system operation, but avoid logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). + config.log_level = :info # Prepend all log lines with the following tags. config.log_tags = [ :request_id ] @@ -48,23 +43,23 @@ # Use a different cache store in production. # config.cache_store = :mem_cache_store - # Use a real queuing backend for Active Job (and separate queues per environment) + # Use a real queuing backend for Active Job (and separate queues per environment). # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "shizoid_#{Rails.env}" + # config.active_job.queue_name_prefix = "shizoid_production" # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify + # Don't log any deprecations. + config.active_support.report_deprecations = false # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new # Use a different logger for distributed setups. - # require 'syslog/logger' - # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') + # require "syslog/logger" + # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name") if ENV["RAILS_LOG_TO_STDOUT"].present? logger = ActiveSupport::Logger.new(STDOUT) diff --git a/config/environments/test.rb b/config/environments/test.rb index 781d998..0eb0fe6 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,30 +1,31 @@ -Rails.application.configure do - # Make bots stubbed before processing routes.rb: - Telegram.reset_bots - Telegram::Bot::ClientStub.stub_all! +require "active_support/core_ext/integer/time" + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! +Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # The test environment is used exclusively to run your application's - # test suite. You never need to work with it otherwise. Remember that - # your test database is "scratch space" for the test suite and is wiped - # and recreated between test runs. Don't rely on the data there! + # Turn false under Spring and add config.action_view.cache_template_loading = true config.cache_classes = true - # Do not eager load code on boot. This avoids loading your whole application - # just for the purpose of running a single test. If you are using a tool that - # preloads Rails for running tests, you may have to set it to true. - config.eager_load = false + # Eager loading loads your whole application. When running a single test locally, + # this probably isn't necessary. It's a good idea to do in a continuous integration + # system, or in some way before deploying your code. + config.eager_load = ENV["CI"].present? # Configure public file server for tests with Cache-Control for performance. config.public_file_server.enabled = true config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" + "Cache-Control" => "public, max-age=#{1.hour.to_i}" } # Show full error reports and disable caching. config.consider_all_requests_local = true config.action_controller.perform_caching = false + config.cache_store = :null_store # Raise exceptions instead of rendering exception templates. config.action_dispatch.show_exceptions = false @@ -35,6 +36,15 @@ # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true end diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb index 51639b6..f4556db 100644 --- a/config/initializers/application_controller_renderer.rb +++ b/config/initializers/application_controller_renderer.rb @@ -1,6 +1,9 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. -# ApplicationController.renderer.defaults.merge!( -# http_host: 'example.org', -# https: false -# ) +# ActiveSupport::Reloader.to_prepare do +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) +# end diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb index 59385cd..74f30e8 100644 --- a/config/initializers/backtrace_silencers.rb +++ b/config/initializers/backtrace_silencers.rb @@ -1,7 +1,10 @@ +# frozen_string_literal: true + # Be sure to restart your server when you modify this file. # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. -# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } +# Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) } -# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. -# Rails.backtrace_cleaner.remove_silencers! +# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code +# by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'". +Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"] diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb index 3b1c1b5..e5a82f1 100644 --- a/config/initializers/cors.rb +++ b/config/initializers/cors.rb @@ -7,9 +7,9 @@ # Rails.application.config.middleware.insert_before 0, Rack::Cors do # allow do -# origins 'example.com' +# origins "example.com" # -# resource '*', +# resource "*", # headers: :any, # methods: [:get, :post, :put, :patch, :delete, :options, :head] # end diff --git a/config/initializers/extensions/telegram/bot/client.rb b/config/initializers/extensions/telegram/bot/client.rb new file mode 100644 index 0000000..6347b8f --- /dev/null +++ b/config/initializers/extensions/telegram/bot/client.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Monkeypatch gem to return plain json in polling mode + +module Telegram + module Bot + class Client + def fetch_updates + response = api.getUpdates(options) + return unless response['ok'] + + response['result'].each do |data| + update = Types::Update.new(data) + @options[:offset] = update.update_id.next + log_incoming_message(data) + yield data + end + rescue Faraday::TimeoutError + retry + end + end + end +end diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb index 4a994e1..4b34a03 100644 --- a/config/initializers/filter_parameter_logging.rb +++ b/config/initializers/filter_parameter_logging.rb @@ -1,4 +1,6 @@ # Be sure to restart your server when you modify this file. # Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [:password] +Rails.application.config.filter_parameters += [ + :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn +] diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ac033bf..3860f65 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -4,13 +4,13 @@ # are locale specific, and you may define rules for as many different # locales as you wish. All of these examples are active by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.plural /^(ox)$/i, '\1en' -# inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' +# inflect.plural /^(ox)$/i, "\\1en" +# inflect.singular /^(ox)en/i, "\\1" +# inflect.irregular "person", "people" # inflect.uncountable %w( fish sheep ) # end # These inflection rules are supported but not enabled by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.acronym 'RESTful' +# inflect.acronym "RESTful" # end diff --git a/config/initializers/locale.rb b/config/initializers/locale.rb new file mode 100644 index 0000000..ce1778f --- /dev/null +++ b/config/initializers/locale.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +Rails.application.config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s] +# config.i18n.available_locales = %i[ru_RU en_US] +Rails.application.config.i18n.available_locales = %i[ru_RU] +Rails.application.config.i18n.default_locale = :ru_RU diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb index dc18996..6e1d16f 100644 --- a/config/initializers/mime_types.rb +++ b/config/initializers/mime_types.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Add new mime types for use in respond_to blocks: diff --git a/config/initializers/new_framework_defaults_6_1.rb b/config/initializers/new_framework_defaults_6_1.rb new file mode 100644 index 0000000..a128fd3 --- /dev/null +++ b/config/initializers/new_framework_defaults_6_1.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 6.1 upgrade. +# +# Once upgraded flip defaults one by one to migrate to the new default. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. + +# Support for inversing belongs_to -> has_many Active Record associations. +# Rails.application.config.active_record.has_many_inversing = true + +# Track Active Storage variants in the database. +# Rails.application.config.active_storage.track_variants = true + +# Apply random variation to the delay when retrying failed jobs. +# Rails.application.config.active_job.retry_jitter = 0.15 + +# Stop executing `after_enqueue`/`after_perform` callbacks if +# `before_enqueue`/`before_perform` respectively halts with `throw :abort`. +# Rails.application.config.active_job.skip_after_callbacks_if_terminated = true + +# Specify cookies SameSite protection level: either :none, :lax, or :strict. +# +# This change is not backwards compatible with earlier Rails versions. +# It's best enabled when your entire app is migrated and stable on 6.1. +# Rails.application.config.action_dispatch.cookies_same_site_protection = :lax + +# Generate CSRF tokens that are encoded in URL-safe Base64. +# +# This change is not backwards compatible with earlier Rails versions. +# It's best enabled when your entire app is migrated and stable on 6.1. +# Rails.application.config.action_controller.urlsafe_csrf_tokens = true + +# Specify whether `ActiveSupport::TimeZone.utc_to_local` returns a time with an +# UTC offset or a UTC time. +# ActiveSupport.utc_to_local_returns_utc_offset_times = true + +# Change the default HTTP status code to `308` when redirecting non-GET/HEAD +# requests to HTTPS in `ActionDispatch::SSL` middleware. +# Rails.application.config.action_dispatch.ssl_default_redirect_status = 308 + +# Use new connection handling API. For most applications this won't have any +# effect. For applications using multiple databases, this new API provides +# support for granular connection swapping. +# Rails.application.config.active_record.legacy_connection_handling = false + +# Make `form_with` generate non-remote forms by default. +# Rails.application.config.action_view.form_with_generates_remote_forms = false + +# Set the default queue name for the analysis job to the queue adapter default. +# Rails.application.config.active_storage.queues.analysis = nil + +# Set the default queue name for the purge job to the queue adapter default. +# Rails.application.config.active_storage.queues.purge = nil + +# Set the default queue name for the incineration job to the queue adapter default. +# Rails.application.config.action_mailbox.queues.incineration = nil + +# Set the default queue name for the routing job to the queue adapter default. +# Rails.application.config.action_mailbox.queues.routing = nil + +# Set the default queue name for the mail deliver job to the queue adapter default. +# Rails.application.config.action_mailer.deliver_later_queue_name = nil diff --git a/config/initializers/new_framework_defaults_7_0.rb b/config/initializers/new_framework_defaults_7_0.rb new file mode 100644 index 0000000..a579326 --- /dev/null +++ b/config/initializers/new_framework_defaults_7_0.rb @@ -0,0 +1,117 @@ +# Be sure to restart your server when you modify this file. +# +# This file eases your Rails 7.0 framework defaults upgrade. +# +# Uncomment each configuration one by one to switch to the new default. +# Once your application is ready to run with all new defaults, you can remove +# this file and set the `config.load_defaults` to `7.0`. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. +# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html + +# `button_to` view helper will render `