Skip to content

Commit 191e3f7

Browse files
committed
Bot joins channel
1 parent 0e68cf7 commit 191e3f7

12 files changed

Lines changed: 717 additions & 135 deletions

File tree

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,5 @@ Bugsnag, по-возмоности указывая контекст котор
123123

124124
План имплементации всегда имеет такой-же номер как и спецификаций по которой он
125125
создан и имеет ссылку на неё.
126+
127+
После выполнения плана имплементации отмечай выполненные пункты как выполненные.

app/jobs/channels/bot_join_job.rb

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Фоновая задача для вступления бота в Telegram канал
2+
# Запускается после добавления канала в систему
3+
class Channels::BotJoinJob < ApplicationJob
4+
queue_as :channels
5+
retry_on StandardError, wait: :exponentially_longer, attempts: 3
6+
7+
def perform(channel_id)
8+
with_error_context(channel_id: channel_id, action: 'bot_join') do
9+
channel = Channel.find(channel_id)
10+
11+
Rails.logger.info "Starting bot join process for channel #{channel.username}"
12+
13+
# Обновляем статус на joining
14+
channel.start_joining!
15+
16+
# Пытаемся вступить в канал
17+
success = attempt_to_join_channel(channel)
18+
19+
if success
20+
channel.mark_as_joined!
21+
notify_admins_success(channel)
22+
Rails.logger.info "Bot successfully joined channel #{channel.username}"
23+
else
24+
error_message = extract_error_message(success)
25+
channel.mark_as_join_failed!(error_message)
26+
notify_admins_failure(channel, error_message)
27+
Rails.logger.error "Bot failed to join channel #{channel.username}: #{error_message}"
28+
end
29+
end
30+
end
31+
32+
private
33+
34+
def attempt_to_join_channel(channel)
35+
begin
36+
# Инициализируем клиент
37+
bot = Telegram.bots[:default]
38+
39+
# Для публичных каналов бот может просто начать следить
40+
# через webhook без дополнительного вступления
41+
chat_id = channel.telegram_id
42+
43+
# Проверяем доступность канала
44+
chat_info = bot.get_chat(chat_id: chat_id)
45+
46+
if chat_info['ok']
47+
Rails.logger.info "Channel #{channel.username} is accessible to bot"
48+
true
49+
else
50+
error_code = chat_info['error_code'] || 'unknown'
51+
error_description = chat_info['description'] || 'Unknown error'
52+
53+
Rails.logger.warn "Cannot access channel #{channel.username}: #{error_code} - #{error_description}"
54+
{ error_code: error_code, error_description: error_description }
55+
end
56+
57+
rescue Telegram::Bot::Error => e
58+
Rails.logger.error "Telegram API error for channel #{channel.username}: #{e.message}"
59+
{ error_code: 'telegram_api_error', error_description: e.message }
60+
rescue StandardError => e
61+
Rails.logger.error "Unexpected error for channel #{channel.username}: #{e.message}"
62+
{ error_code: 'unexpected_error', error_description: e.message }
63+
end
64+
end
65+
66+
def extract_error_message(result)
67+
return 'Unknown error' if result == true
68+
return result[:error_description] if result.is_a?(Hash) && result[:error_description]
69+
'Failed to join channel'
70+
end
71+
72+
def notify_admins_success(channel)
73+
# TODO: Implement admin notifications in stage 4
74+
Rails.logger.info "Bot successfully joined channel: #{channel.username} (#{channel.title})"
75+
end
76+
77+
def notify_admins_failure(channel, error_message)
78+
# TODO: Implement admin notifications in stage 4
79+
Rails.logger.error "Bot failed to join channel #{channel.username}: #{error_message}"
80+
end
81+
end

app/jobs/channels/fetch_posts_job.rb

Lines changed: 0 additions & 84 deletions
This file was deleted.

app/jobs/channels/monitor_job.rb

Lines changed: 0 additions & 34 deletions
This file was deleted.

app/models/channel.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ class Channel < ApplicationRecord
99
validates :telegram_id, presence: true, uniqueness: true
1010
validates :username, presence: true, uniqueness: true
1111

12+
# Enums
13+
enum bot_join_status: {
14+
not_joined: 0,
15+
joining: 1,
16+
joined: 2,
17+
join_failed: 3
18+
}
19+
1220
# Scopes
1321
scope :active, -> { where(deactivated_at: nil) }
1422
scope :inactive, -> { where.not(deactivated_at: nil) }
@@ -17,6 +25,12 @@ class Channel < ApplicationRecord
1725
scope :recently_updated, -> { where('last_post_at > ?', 24.hours.ago) }
1826
scope :needs_monitoring, -> { where('monitored_at IS NULL OR monitored_at < ?', 10.minutes.ago) }
1927

28+
# Bot join status scopes
29+
scope :joined, -> { where(bot_join_status: 'joined') }
30+
scope :not_joined, -> { where(bot_join_status: 'not_joined') }
31+
scope :joining, -> { where(bot_join_status: 'joining') }
32+
scope :join_failed, -> { where(bot_join_status: 'join_failed') }
33+
2034
# Methods
2135
def mark_as_monitored!
2236
touch(:monitored_at)
@@ -37,4 +51,21 @@ def activate!
3751
def active?
3852
deactivated_at.nil?
3953
end
54+
55+
# Bot join status methods
56+
def start_joining!
57+
update!(bot_join_status: 'joining')
58+
end
59+
60+
def mark_as_joined!
61+
update!(bot_join_status: 'joined', bot_join_at: Time.current, bot_join_error: nil)
62+
end
63+
64+
def mark_as_join_failed!(error_message)
65+
update!(bot_join_status: 'join_failed', bot_join_error: error_message)
66+
end
67+
68+
def bot_can_monitor?
69+
active? && joined?
70+
end
4071
end

app/services/telegram/channel_service.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,21 @@ def add_channel_to_database(channel_username)
141141
message: I18n.t('telegram_bot.channels.add.error', error: channel.errors.full_messages.join(', '))
142142
}
143143
end
144+
145+
# Запускаем задачу для вступления бота в канал
146+
Channels::BotJoinJob.perform_later(channel.id)
144147
else
145148
# Обновляем информацию о канале
146149
channel.update(
147150
title: channel_info[:title],
148151
description: channel_info[:description],
149152
subscribers_count: channel_info[:member_count]
150153
)
154+
155+
# Если канал еще не вступал, запускаем задачу для вступления
156+
if channel.not_joined?
157+
Channels::BotJoinJob.perform_later(channel.id)
158+
end
151159
end
152160

153161
{
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class AddBotJoinFieldsToChannels < ActiveRecord::Migration[8.0]
2+
def change
3+
add_column :channels, :bot_join_status, :string, default: 'not_joined', null: false
4+
add_column :channels, :bot_join_error, :text
5+
add_column :channels, :bot_join_at, :datetime
6+
7+
add_index :channels, :bot_join_status
8+
end
9+
end

db/schema.rb

Lines changed: 23 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)