diff --git a/Gemfile b/Gemfile index 0e88c864..c4a137f7 100644 --- a/Gemfile +++ b/Gemfile @@ -84,3 +84,7 @@ gem 'turbo-rails' gem 'stimulus-rails' gem 'will_paginate' gem 'telegram-bot-ruby', '~> 1.0' +gem 'delayed_job_active_record' +gem 'daemons' + +gem "activejob-status", "~> 1.0" diff --git a/Gemfile.lock b/Gemfile.lock index 4f4d3531..b76dea16 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -49,6 +49,9 @@ GEM activejob (6.1.7.6) activesupport (= 6.1.7.6) globalid (>= 0.3.6) + activejob-status (1.0.0) + activejob (>= 6.0) + activesupport (>= 6.0) activemodel (6.1.7.6) activesupport (= 6.1.7.6) activerecord (6.1.7.6) @@ -140,6 +143,7 @@ GEM concurrent-ruby (1.2.2) connection_pool (2.4.1) crass (1.0.6) + daemons (1.4.1) database_cleaner (2.0.2) database_cleaner-active_record (>= 2, < 3) database_cleaner-active_record (2.1.0) @@ -148,6 +152,11 @@ GEM database_cleaner-core (2.0.1) date (3.3.3) debug_inspector (1.1.0) + delayed_job (4.1.11) + activesupport (>= 3.0, < 8.0) + delayed_job_active_record (4.1.8) + activerecord (>= 3.0, < 8.0) + delayed_job (>= 3.0, < 5) devise (4.9.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -524,6 +533,7 @@ PLATFORMS ruby DEPENDENCIES + activejob-status (~> 1.0) bcrypt_pbkdf better_errors binding_of_caller @@ -540,7 +550,9 @@ DEPENDENCIES capybara-screenshot ci_reporter_rspec coffee-rails + daemons database_cleaner + delayed_job_active_record devise devise_invitable dotenv-rails diff --git a/app/controllers/bots_controller.rb b/app/controllers/bots_controller.rb index 5b41c6e5..5bac86dd 100644 --- a/app/controllers/bots_controller.rb +++ b/app/controllers/bots_controller.rb @@ -1,5 +1,5 @@ class BotsController < ApplicationController - before_action :set_bot, only: %i[ show edit update destroy ] + before_action :set_bot, only: %i[ show edit update destroy telegram_listen ] # GET /bots or /bots.json def index @@ -20,6 +20,12 @@ class BotsController < ApplicationController def edit end + def telegram_listen + authorize @bot + @bot.delay.telegram_listen + redirect_to request.referrer + end + # POST /bots or /bots.json def create authorize @bot = Bot.new(bot_params) diff --git a/app/models/bot.rb b/app/models/bot.rb index 19d09f9e..76db0bbc 100644 --- a/app/models/bot.rb +++ b/app/models/bot.rb @@ -1,4 +1,3 @@ -require 'telegram/bot' class Bot < ActiveRecord::Base belongs_to :user belongs_to :profile, optional: true @@ -16,23 +15,26 @@ class Bot < ActiveRecord::Base end def telegram_listen + # `echo "#{Time.now} hi from telegram listen" > tglisten.txt` return unless self.telegram_api_token - Telegram::Bot::Client.run(telegram_api_token) do |bot| - bot.listen do |message| - # TODO Logging - # File.open("./logs/#{@botname}.log", 'a') { |f| f.write(["\n", Time.now, message.chat.id, message.from.id, message.from.first_name, message].join("\t")) } - case message - when Telegram::Bot::Types::Message - # Debugging use next to skip/drop the queue - # next - case message.text - when /^\/fortune/i - response_text = `fortune -a` - begin - bot.api.send_message(chat_id: message.chat.id, reply_to_message_id: message.message_id, text: response_text, parse_mode: "Markdown") - rescue - puts "Error from fortune" - end + bot = Telegram::Bot::Client.new(telegram_api_token) + # Signal.trap('INT') do + # bot.stop + # end + bot.listen do |message| + # TODO Logging + # File.open("./logs/#{@botname}.log", 'a') { |f| f.write(["\n", Time.now, message.chat.id, message.from.id, message.from.first_name, message].join("\t")) } + case message + when Telegram::Bot::Types::Message + # Debugging use next to skip/drop the queue + # next + case message.text + when /^\/fortune/i + response_text = `fortune -a` + begin + bot.api.send_message(chat_id: message.chat.id, reply_to_message_id: message.message_id, text: response_text, parse_mode: "Markdown") + rescue + puts "Error from fortune" end end end diff --git a/app/policies/bot_policy.rb b/app/policies/bot_policy.rb index 12536a41..76d87bc0 100644 --- a/app/policies/bot_policy.rb +++ b/app/policies/bot_policy.rb @@ -33,6 +33,12 @@ class BotPolicy @user.admin? || @user == @bot.user end + # TODO: Restrict bot start but for now open to all + def telegram_listen? + @user.admin? || @user == @bot.user || true + end + + def new? @user.admin? end diff --git a/app/views/bots/show.html.haml b/app/views/bots/show.html.haml index c0edeea1..2ac10a2e 100644 --- a/app/views/bots/show.html.haml +++ b/app/views/bots/show.html.haml @@ -8,6 +8,8 @@ %p %b Telegram api token: = @bot.telegram_api_token + - if policy(@bot).telegram_listen? + %br= link_to 'Start', telegram_listen_bot_path(@bot) %p %b Discord api token: = @bot.discord_api_token @@ -19,3 +21,4 @@ %b Profile: = link_to @bot.profile.display_name, @bot.profile + diff --git a/bin/delayed_job b/bin/delayed_job new file mode 100755 index 00000000..edf19598 --- /dev/null +++ b/bin/delayed_job @@ -0,0 +1,5 @@ +#!/usr/bin/env ruby + +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) +require 'delayed/command' +Delayed::Command.new(ARGV).daemonize diff --git a/config/application.rb b/config/application.rb index e2ee8c40..77136470 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,6 +1,7 @@ require_relative "boot" require "rails/all" +require 'telegram/bot' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. @@ -18,5 +19,6 @@ module MyTendyZone # # config.time_zone = "Central Time (US & Canada)" # config.eager_load_paths << Rails.root.join("extras") + config.active_job.queue_adapter = :delayed_job end end diff --git a/config/initializers/activejob_status.rb b/config/initializers/activejob_status.rb new file mode 100644 index 00000000..1af33eea --- /dev/null +++ b/config/initializers/activejob_status.rb @@ -0,0 +1,5 @@ +# Select what data you want to store. +# Available options are: :status, :serialized_job, :exception +# Default is [:status] +# +ActiveJob::Status.options = { includes: %i[status exception] } diff --git a/config/routes.rb b/config/routes.rb index 8ef25716..560e9fee 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,9 @@ Rails.application.routes.draw do - resources :bots + resources :bots do + member do + get 'telegram_listen' + end + end resources :social_links resources :profiles do member do diff --git a/db/migrate/20231128174336_create_delayed_jobs.rb b/db/migrate/20231128174336_create_delayed_jobs.rb new file mode 100644 index 00000000..d55c28bc --- /dev/null +++ b/db/migrate/20231128174336_create_delayed_jobs.rb @@ -0,0 +1,22 @@ +class CreateDelayedJobs < ActiveRecord::Migration[6.1] + def self.up + create_table :delayed_jobs do |table| + table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue + table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually. + table.text :handler, null: false # YAML-encoded string of the object that will do work + table.text :last_error # reason for last failure (See Note below) + table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. + table.datetime :locked_at # Set when a client is working on this object + table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) + table.string :locked_by # Who is working on this object (if locked) + table.string :queue # The name of the queue this job is in + table.timestamps null: true + end + + add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority" + end + + def self.down + drop_table :delayed_jobs + end +end diff --git a/db/schema.rb b/db/schema.rb index 16b45bce..fd6b306c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_11_26_154137) do +ActiveRecord::Schema.define(version: 2023_11_28_174336) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -36,6 +36,21 @@ ActiveRecord::Schema.define(version: 2023_11_26_154137) do t.index ["user_id"], name: "index_canisters_on_user_id" end + create_table "delayed_jobs", force: :cascade do |t| + t.integer "priority", default: 0, null: false + t.integer "attempts", default: 0, null: false + t.text "handler", null: false + t.text "last_error" + t.datetime "run_at" + t.datetime "locked_at" + t.datetime "failed_at" + t.string "locked_by" + t.string "queue" + t.datetime "created_at", precision: 6 + t.datetime "updated_at", precision: 6 + t.index ["priority", "run_at"], name: "delayed_jobs_priority" + end + create_table "doodads", force: :cascade do |t| t.text "body" t.bigint "user_id"