telegram_chatbot 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +1 -0
  4. data/Rakefile +22 -0
  5. data/app/assets/config/telegram_chatbot_manifest.js +4 -0
  6. data/app/assets/images/telegram_chatbot/telegram_bot/bot_father.png +0 -0
  7. data/app/assets/images/telegram_chatbot/telegram_bot/bot_name.png +0 -0
  8. data/app/assets/images/telegram_chatbot/telegram_bot/bot_token.png +0 -0
  9. data/app/assets/images/telegram_chatbot/telegram_bot/bot_username.png +0 -0
  10. data/app/assets/images/telegram_chatbot/telegram_bot/group_step1.png +0 -0
  11. data/app/assets/images/telegram_chatbot/telegram_bot/group_step2.png +0 -0
  12. data/app/assets/images/telegram_chatbot/telegram_bot/group_step3.png +0 -0
  13. data/app/assets/images/telegram_chatbot/telegram_bot/group_step4.png +0 -0
  14. data/app/assets/images/telegram_chatbot/telegram_bot/group_step5.png +0 -0
  15. data/app/assets/images/telegram_chatbot/telegram_bot/newbot.png +0 -0
  16. data/app/assets/images/telegram_chatbot/telegram_bot/setting.png +0 -0
  17. data/app/assets/images/telegram_chatbot/telegram_bot/telegram_off.png +0 -0
  18. data/app/assets/images/telegram_chatbot/telegram_bot/telegram_on.png +0 -0
  19. data/app/assets/javascripts/telegram_chatbot/application.js +9 -0
  20. data/app/assets/stylesheets/telegram_chatbot/application.scss +18 -0
  21. data/app/assets/stylesheets/telegram_chatbot/telegram_bots.scss +97 -0
  22. data/app/controllers/telegram_chatbot/application_controller.rb +5 -0
  23. data/app/controllers/telegram_chatbot/telegram_bots_controller.rb +32 -0
  24. data/app/controllers/telegram_chatbot/webhooks_controller.rb +38 -0
  25. data/app/helpers/telegram_chatbot/application_helper.rb +24 -0
  26. data/app/helpers/telegram_chatbot/telegram_bots_helper.rb +4 -0
  27. data/app/jobs/telegram_chatbot/application_job.rb +4 -0
  28. data/app/mailers/telegram_chatbot/application_mailer.rb +6 -0
  29. data/app/models/telegram_chatbot/application_record.rb +5 -0
  30. data/app/models/telegram_chatbot/chat_group.rb +18 -0
  31. data/app/models/telegram_chatbot/notification.rb +26 -0
  32. data/app/models/telegram_chatbot/telegram_bot.rb +22 -0
  33. data/app/views/telegram_chatbot/telegram_bots/help.haml +72 -0
  34. data/app/views/telegram_chatbot/telegram_bots/show.haml +25 -0
  35. data/app/workers/telegram_chatbot/notification_worker.rb +13 -0
  36. data/config/initializers/assets.rb +1 -0
  37. data/config/initializers/simple_form.rb +176 -0
  38. data/config/initializers/simple_form_bootstrap.rb +440 -0
  39. data/config/initializers/telegram.rb +9 -0
  40. data/config/locales/share.en.yml +37 -0
  41. data/config/locales/share.km.yml +37 -0
  42. data/config/locales/simple_form.en.yml +31 -0
  43. data/config/locales/telegram_bot.en.yml +38 -0
  44. data/config/locales/telegram_bot.km.yml +38 -0
  45. data/config/routes.rb +11 -0
  46. data/db/migrate/20210425084114_create_telegram_chatbot_telegram_bots.rb +12 -0
  47. data/db/migrate/20210425084127_create_telegram_chatbot_chat_groups.rb +16 -0
  48. data/lib/tasks/telegram_chatbot_tasks.rake +4 -0
  49. data/lib/telegram_chatbot/engine.rb +9 -0
  50. data/lib/telegram_chatbot/version.rb +3 -0
  51. data/lib/telegram_chatbot.rb +10 -0
  52. data/lib/templates/erb/scaffold/_form.html.erb +15 -0
  53. metadata +270 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: daf944123265ec4629b1d31d92d739c0c127f632e4ec0d78cabb4f6eae41931c
4
+ data.tar.gz: 8bc181f944b381fe4e3c62a09be1327e766c0b214629b4b68c6d00d54c74d821
5
+ SHA512:
6
+ metadata.gz: 771b4b94e55762242261cbfa0f7e6e387e8168914b7909b0765d0fefa679f43966c7ea146b5f9d6d1521b5900bd7b9286be8bb5a0ca855352c7d93735ac8bc6f
7
+ data.tar.gz: d3aac21df88d133e78c78f4ab5460cf26589c601e5baef5f65fc3c7da9b9eccb84a4841efccc3f6da923a579fa8be4685ce25469743d710ac51a0bb420727455
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2021
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # telegram_chatbot
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'TelegramChatbot'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
@@ -0,0 +1,4 @@
1
+ //= link_directory ../stylesheets/telegram_chatbot .css
2
+ //= link_directory ../stylesheets/telegram_chatbot .scss
3
+ //= link_directory ../javascripts/telegram_chatbot .js
4
+ //= link_tree ../images
@@ -0,0 +1,9 @@
1
+ //= require rails-ujs
2
+
3
+ //= require jquery3
4
+ //= require popper
5
+ //= require bootstrap
6
+
7
+ $( document ).ready(function() {
8
+ $('[data-toggle="tooltip"]').tooltip();
9
+ });
@@ -0,0 +1,18 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ * require_tree .
14
+ * require_self
15
+ */
16
+
17
+ @import "bootstrap";
18
+ @import "telegram_chatbot/telegram_bots";
@@ -0,0 +1,97 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
5
+
6
+ #telegram_chatbot {
7
+ .form-check {
8
+ position: relative;
9
+ display: block;
10
+ margin-bottom: 0.75rem;
11
+ padding-left: 0;
12
+ }
13
+
14
+ .form-check-toggle {
15
+ position: relative;
16
+ padding-left: 0;
17
+ line-height: 24px;
18
+
19
+ input {
20
+ display: block;
21
+ position: absolute;
22
+ top: 0;
23
+ right: 0;
24
+ bottom: 0;
25
+ left: 0;
26
+ width: 0%;
27
+ height: 0%;
28
+ margin: 0;
29
+ cursor: pointer;
30
+ opacity: 0;
31
+ filter: alpha(opacity=0);
32
+ }
33
+
34
+ input + span {
35
+ cursor: pointer;
36
+ user-select: none;
37
+ height: 24px;
38
+ margin-left: 70px;
39
+ display: block;
40
+
41
+ &:before {
42
+ content: '';
43
+ position: absolute;
44
+ left: 0;
45
+ display: inline-block;
46
+ height: 24px;
47
+ width: 44px;
48
+ background: #FFF;
49
+ border: solid 1px #eff2f3;
50
+ //box-shadow: inset 0 0 5px $engie-gray-10;
51
+ transition: background 0.3s ease-in-out, border-color 0.3s ease-in-out;
52
+ border-radius: 15px;
53
+ }
54
+
55
+ &:after {
56
+ width: 22px;
57
+ height: 22px;
58
+ margin-top: 1px;
59
+ margin-left: 1px;
60
+ border-radius: 50%;
61
+ position: absolute;
62
+ left: 0;
63
+ top: 0;
64
+ display: block;
65
+ background: #FFF;
66
+ transition: margin-left 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
67
+ text-align: center;
68
+ font-weight: bold;
69
+ content: '';
70
+ //border: solid $engie-black-01 0.05rem;
71
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.2), 0 0 5px rgba(0, 0, 0, 0.05);
72
+ }
73
+ }
74
+
75
+ input {
76
+ &:checked + span {
77
+ &:after {
78
+ content: '';
79
+ margin-left: 21px;
80
+ box-shadow: none;
81
+ }
82
+
83
+ &:before {
84
+ background-color: #86be4e;
85
+ border-color: #86be4e;
86
+ transition: background 0.3s ease-in-out, border-color 0.3s ease-in-out;
87
+ }
88
+ }
89
+ }
90
+ }
91
+
92
+ .image {
93
+ max-width: 600px;
94
+ margin: 20px 0;
95
+ }
96
+
97
+ }
@@ -0,0 +1,5 @@
1
+ module TelegramChatbot
2
+ class ApplicationController < ::ApplicationController
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,32 @@
1
+ require_dependency "telegram_chatbot/application_controller"
2
+
3
+ module TelegramChatbot
4
+ class TelegramBotsController < ApplicationController
5
+ before_action :set_bot, on: [:show, :upsert]
6
+
7
+ def show
8
+ end
9
+
10
+ def upsert
11
+ if @telegram_bot.update(bot_params)
12
+ redirect_to telegram_bots_url
13
+ else
14
+ render :show
15
+ end
16
+ end
17
+
18
+ def help
19
+ end
20
+
21
+ private
22
+ def set_bot
23
+ @telegram_bot = TelegramChatbot::TelegramBot.first || TelegramChatbot::TelegramBot.new
24
+ end
25
+
26
+ def bot_params
27
+ params.require(:telegram_bot).permit(
28
+ :token, :username, :enabled
29
+ )
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ module TelegramChatbot
2
+ class WebhooksController < ::Telegram::Bot::UpdatesController
3
+ include ::Telegram::Bot::UpdatesController::MessageContext
4
+
5
+ def message(message)
6
+ return migrate_chat_group(message) if message["migrate_to_chat_id"].present?
7
+ return unless managing_member?(message)
8
+
9
+ upsert_chat_group(message)
10
+ end
11
+
12
+ private
13
+ def migrate_chat_group(message)
14
+ group = TelegramChatbot::ChatGroup.find_by(chat_id: message["chat"]["id"].to_s)
15
+ return if group.nil?
16
+
17
+ group.update(chat_id: message["migrate_to_chat_id"], chat_type: TelegramChatbot::ChatGroup::TELEGRAM_SUPER_GROUP)
18
+ end
19
+
20
+ def managing_member?(message)
21
+ @member = message["left_chat_member"] || message["new_chat_member"]
22
+ @member.present? && @member["is_bot"] && TelegramChatbot::ChatGroup::TELEGRAM_CHAT_TYPES.include?(message["chat"]["type"])
23
+ end
24
+
25
+ def upsert_chat_group(message)
26
+ group = ::TelegramChatbot::ChatGroup.find_or_initialize_by(chat_id: message["chat"]["id"].to_s, bot_token: bot.token)
27
+ group.update(
28
+ title: message["chat"]["title"],
29
+ actived: message["new_chat_member"].present?,
30
+ chat_type: message["chat"]["type"]
31
+ )
32
+ end
33
+
34
+ def bot
35
+ @bot ||= ::TelegramChatbot::TelegramBot.first
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ module TelegramChatbot
2
+ module ApplicationHelper
3
+ def form_check_toggle(option = {})
4
+ disabled = option[:disabled].present? ? "disabled" : ""
5
+ checked = option[:checked].present? ? "checked" : ""
6
+
7
+ str = "<div class='form-check'>"
8
+ str += "<label class='form-check-label form-check-toggle'>"
9
+ str += "<input type='hidden' name='#{option[:name]}' value='0'/>"
10
+ str += "<input type='checkbox' name='#{option[:name]}' #{checked} #{disabled}/>"
11
+ str += "<span>#{option[:label]}</span>"
12
+ str += "</label>"
13
+ str + "</div>"
14
+ end
15
+
16
+ def method_missing(method, *args, &block)
17
+ if (method.to_s.end_with?('_path') || method.to_s.end_with?('_url')) && main_app.respond_to?(method)
18
+ main_app.send(method, *args)
19
+ else
20
+ super
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,4 @@
1
+ module TelegramChatbot
2
+ module TelegramBotsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module TelegramChatbot
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module TelegramChatbot
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module TelegramChatbot
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TelegramChatbot
4
+ class ChatGroup < ApplicationRecord
5
+ TELEGRAM_CHAT_TYPES = %w[group supergroup]
6
+ TELEGRAM_SUPER_GROUP = "supergroup"
7
+ TELEGRAM_GROUP = "group"
8
+
9
+ belongs_to :telegram_bot, primary_key: "token", foreign_key: "bot_token"
10
+
11
+ scope :actives, -> { where(actived: true) }
12
+
13
+ validates :title, presence: true
14
+ validates :chat_id, presence: true
15
+ validates :chat_type, presence: true
16
+ validates :chat_type, inclusion: { in: TELEGRAM_CHAT_TYPES }
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ module TelegramChatbot
2
+ class Notification
3
+ def self.notify_async(message, groups=[])
4
+ return unless bot.present? && bot.actived?
5
+
6
+ NotificationWorker.perform_async(message, groups)
7
+ end
8
+
9
+ def self.notify(message, groups=[])
10
+ groups.each do |group|
11
+ client.send_message(chat_id: group.chat_id, text: message, parse_mode: :HTML)
12
+ rescue ::Telegram::Bot::Forbidden => e
13
+ group.update(actived: false, reason: e)
14
+ end
15
+ end
16
+
17
+ private
18
+ def self.client
19
+ ::Telegram::Bot::Client.new(bot.token, bot.username)
20
+ end
21
+
22
+ def self.bot
23
+ ::TelegramChatbot::TelegramBot.first
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module TelegramChatbot
2
+ class TelegramBot < ApplicationRecord
3
+ validates :token, :username, presence: true, if: :enabled?
4
+
5
+ before_create :post_webhook_to_telegram, if: :enabled?
6
+ before_update :post_webhook_to_telegram, if: :enabled?
7
+
8
+ has_many :chat_groups, foreign_key: :bot_token, primary_key: :token
9
+
10
+ def post_webhook_to_telegram
11
+ bot = Telegram::Bot::Client.new(token: token, username: username)
12
+
13
+ begin
14
+ request = bot.set_webhook(url: ENV["TELEGRAM_CALLBACK_URL"])
15
+
16
+ self.actived = request["ok"]
17
+ rescue
18
+ self.actived = false
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,72 @@
1
+ #contents
2
+ %nav.navbar.navbar-expand-lg.navbar-light.bg-light
3
+ .container-fluid
4
+ %h3= t('telegram_bot.telegram_bot')
5
+ - done_url = root_url
6
+ = link_to t('shared.done'), done_url, class: 'btn btn-light'
7
+
8
+ .medium-width
9
+ %p= t('telegram_bot.you_can_configure_your_bot')
10
+
11
+ %div
12
+ %h5= t('telegram_bot.visit_bot_father_to_create_newbot')
13
+ %div
14
+ = link_to t('telegram_bot.create_new_bot'), 'https://telegram.me/botfather', class: 'btn btn-link'
15
+ %span= t('telegram_bot.using_the_bot_father')
16
+ = image_tag 'telegram_chatbot/telegram_bot/bot_father.png', class: 'image'
17
+
18
+ %div
19
+ %h5= t('telegram_bot.create_new_bot')
20
+ %div= "#{t('telegram_bot.to_create_a_newbot')}, #{t('telegram_bot.send_command')} <code>\/newbot</code>.".html_safe
21
+ = image_tag 'telegram_chatbot/telegram_bot/newbot.png', class: 'image'
22
+
23
+ %div
24
+ %h5= t('telegram_bot.specify_a_friendly_name')
25
+ %div= t('telegram_bot.give_the_bot_a_friendly_name')
26
+ = image_tag 'telegram_chatbot/telegram_bot/bot_name.png', class: 'image'
27
+
28
+ %div
29
+ %h5= t('telegram_bot.specify_a_username')
30
+ %div= t('telegram_bot.give_the_bot_a_unique_username')
31
+ %div= t('telegram_bot.username_note')
32
+ = image_tag 'telegram_chatbot/telegram_bot/bot_username.png', class: 'image'
33
+
34
+ %div
35
+ %h5= t('telegram_bot.copy_the_access_token')
36
+ %div= t('telegram_bot.copy_the_bot_access_token')
37
+ %div= t('telegram_bot.token_note').html_safe
38
+ = image_tag 'telegram_chatbot/telegram_bot/bot_token.png', class: 'image'
39
+
40
+ %div
41
+ %h5= t('telegram_bot.enter_the_bot_token')
42
+ %div= t('telegram_bot.go_to_setting').html_safe
43
+ = image_tag 'telegram_chatbot/telegram_bot/setting.png', class: 'image'
44
+ %div= t('telegram_bot.enable_telegram_notification').html_safe
45
+ = image_tag 'telegram_chatbot/telegram_bot/telegram_off.png', class: 'image'
46
+ = image_tag 'telegram_chatbot/telegram_bot/telegram_on.png', class: 'image'
47
+
48
+ #group
49
+ %div
50
+ %h2= t('telegram_bot.telegram_chat_group')
51
+ %div= t('telegram_bot.to_add_group_chat')
52
+ = image_tag 'telegram_chatbot/telegram_bot/group_step1.png', class: 'image'
53
+
54
+ %div
55
+ %h5= t('telegram_bot.add_member')
56
+ %div= t('telegram_bot.to_add_member')
57
+ = image_tag 'telegram_chatbot/telegram_bot/group_step2.png', class: 'image'
58
+
59
+ %div
60
+ %h5= t('telegram_bot.search_bot_name')
61
+ %div= t('telegram_bot.to_search_bot_name')
62
+ = image_tag 'telegram_chatbot/telegram_bot/group_step3.png', class: 'image'
63
+
64
+ %div
65
+ %h5= t('telegram_bot.added_to_the_group')
66
+ %div= t('telegram_bot.successfully_added_to_group')
67
+ = image_tag 'telegram_chatbot/telegram_bot/group_step4.png', class: 'image'
68
+
69
+ %div
70
+ %h5= t('telegram_bot.added_group_to_system')
71
+ %div= t('telegram_bot.successfully_added_to_system')
72
+ = image_tag 'telegram_chatbot/telegram_bot/group_step5.png', class: 'image'
@@ -0,0 +1,25 @@
1
+ #telegram_chatbot.modal-content
2
+ = simple_form_for @telegram_bot, url: '/telegram_chatbot/bot/upsert', method: :put, class: 'setting' do |f|
3
+ .modal-header
4
+ .d-flex.align-items-center
5
+ %h5#exampleModalLabel.modal-title
6
+ = t('program.telegram_bot')
7
+ = link_to t('telegram_bot.need_help'), help_telegram_bots_path, class: 'btn btn-link'
8
+
9
+ .modal-body
10
+ .d-flex
11
+ .flex-grow-1
12
+ = form_check_toggle({name: "telegram_bot[enabled]", checked: f.object.enabled, label: t('program.telegram_notification')}).html_safe
13
+ %div
14
+ - if @telegram_bot.present? && @telegram_bot.enabled? && @telegram_bot.actived?
15
+ %i.far.fa-check-circle.text-success{'data-toggle'=> 'tooltip', title: t('telegram_bot.valid_bot')}
16
+ - else
17
+ %i.far.fa-times-circle.text-danger{'data-toggle'=> 'tooltip', title: t('telegram_bot.invalid_bot')}
18
+
19
+ .tokens
20
+ = f.input :username, label: t("telegram_bot.username")
21
+ = f.input :token, label: t("telegram_bot.token")
22
+
23
+ .modal-footer
24
+ = link_to t("shared.cancel"), main_app.root_path, class: 'btn btn-secondary'
25
+ = f.submit t("shared.save"), class: 'btn btn-primary'
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TelegramChatbot
4
+ class NotificationWorker
5
+ include Sidekiq::Worker
6
+
7
+ def perform(message, groups)
8
+ return unless message.present? && groups.present?
9
+
10
+ Notification.notify(message, groups)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1 @@
1
+ Rails.application.config.assets.precompile << "telegram_chatbot_manifest.js"