feat(telegram): add welcome message for new group members

- handler.py: Added handle_new_members() to send customized greetings (supports {name} placeholder, skips bots). - bot_manager.py: Registered new handler using NEW_CHAT_MEMBERS filter. - _10_telegram_bot.py: Integrated _wrap_new_members to ensure the bot uses the latest configuration. - config.html: Added Welcome New Members toggle and Welcome Message input (conditional visibility based on Group Mode). - telegram-config-store.js & default_config.yaml: Added default state (false) and documentation.

keyboardstaff committed Mar 22, 2026 at 10:09 UTC 4a06e0c05ea79b68da7f5984fdbea90de1652e2a
6 files changed +65 -1
plugins/_telegram_integration/default_config.yaml
+2
@@ -9,6 +9,8 @@ bots: []
9 # webhook_secret: "" # optional shared secret for webhook verification
10 # allowed_users: [] # Telegram user IDs or @usernames. Empty = allow all.
11 # group_mode: mention # mention (respond when @mentioned or replied to) | all (respond to every message) | off (ignore groups)
12 +# welcome_enabled: false # Send a greeting when someone joins the group
13 +# welcome_message: "" # Welcome template. Use {name} for the new member's name. Default: "Welcome, {name}!"
14 # user_projects: {} # Map Telegram user_id to project name, e.g. { "123456": "my_project" }
15 # default_project: "" # Fallback project if user not in user_projects
16 # attachment_max_age_hours: 0 # Hours to keep downloaded attachments. 0 = keep forever.
plugins/_telegram_integration/extensions/python/job_loop/_10_telegram_bot.py
+8
@@ -26,6 +26,7 @@ class TelegramBotManager(Extension):
26 handle_clear,
27 handle_message,
28 handle_callback_query,
29 + handle_new_members,
30 cleanup_old_attachments,
31 )
32
@@ -67,6 +68,7 @@ class TelegramBotManager(Extension):
68 _on_clear = partial(_wrap_clear, bot_name=name, bot_cfg=bot_cfg)
69 _on_message = partial(_wrap_message, bot_name=name, bot_cfg=bot_cfg)
70 _on_callback = partial(_wrap_callback, bot_name=name, bot_cfg=bot_cfg)
71 + _on_new_members = partial(_wrap_new_members, bot_name=name, bot_cfg=bot_cfg)
72
73 instance = create_bot(
74 name=name,
@@ -75,6 +77,7 @@ class TelegramBotManager(Extension):
77 on_command_start=_on_start,
78 on_command_clear=_on_clear,
79 on_callback_query=_on_callback,
80 + on_new_members=_on_new_members,
81 group_mode=bot_cfg.get("group_mode", "mention"),
82 )
83
@@ -130,3 +133,8 @@ async def _wrap_message(message, bot_name: str, bot_cfg: dict):
133 async def _wrap_callback(query, bot_name: str, bot_cfg: dict):
134 from plugins._telegram_integration.helpers.handler import handle_callback_query
135 await handle_callback_query(query, bot_name, _get_current_bot_cfg(bot_name) or bot_cfg)
136 +
137 +
138 +async def _wrap_new_members(message, bot_name: str, bot_cfg: dict):
139 + from plugins._telegram_integration.helpers.handler import handle_new_members
140 + await handle_new_members(message, bot_name, _get_current_bot_cfg(bot_name) or bot_cfg)
plugins/_telegram_integration/helpers/bot_manager.py
+5 -1
@@ -4,7 +4,7 @@ from typing import Callable, Awaitable
4
5 from aiogram import Bot, Dispatcher, Router, F
6 from aiogram.client.default import DefaultBotProperties
7 -from aiogram.enums import ParseMode, ChatType
7 +from aiogram.enums import ParseMode, ChatType, ContentType
8 from aiogram.filters import Command, CommandStart
9 from aiogram.types import Message
10
@@ -46,6 +46,7 @@ def create_bot(
46 on_command_start: Callable[..., Awaitable],
47 on_command_clear: Callable[..., Awaitable],
48 on_callback_query: Callable[..., Awaitable] | None = None,
49 + on_new_members: Callable[..., Awaitable] | None = None,
50 group_mode: str = "mention",
51 ) -> BotInstance:
52 bot = Bot(token=token, default=DefaultBotProperties(parse_mode=ParseMode.HTML))
@@ -59,6 +60,9 @@ def create_bot(
60 if on_callback_query:
61 router.callback_query.register(on_callback_query)
62
63 + if on_new_members:
64 + router.message.register(on_new_members, F.content_type == ContentType.NEW_CHAT_MEMBERS)
65 +
66 # Register message handler with group filtering
67 if group_mode == "off":
68 # Private chats only
plugins/_telegram_integration/helpers/handler.py
+25
@@ -289,6 +289,31 @@ async def handle_callback_query(query: CallbackQuery, bot_name: str, bot_cfg: di
289 context.communicate(UserMessage(message=user_msg))
290 save_tmp_chat(context)
291
292 +
293 +async def handle_new_members(message: TgMessage, bot_name: str, bot_cfg: dict):
294 + """Send welcome message when new members join a group."""
295 + if not bot_cfg.get("welcome_enabled", False):
296 + return
297 +
298 + new_members = message.new_chat_members or []
299 + if not new_members:
300 + return
301 +
302 + instance = get_bot(bot_name)
303 + if not instance:
304 + return
305 +
306 + template = bot_cfg.get("welcome_message", "").strip()
307 + if not template:
308 + template = "Welcome, {name}!"
309 +
310 + for member in new_members:
311 + if member.is_bot:
312 + continue
313 + name = member.full_name or member.first_name or str(member.id)
314 + text = template.replace("{name}", name)
315 + await _send_with_temp_bot(instance.bot.token, message.chat.id, text, parse_mode=None)
316 +
317 # Context management
318
319 async def _get_or_create_context(
plugins/_telegram_integration/webui/config.html
+23
@@ -179,6 +179,29 @@
179 </div>
180 </div>
181
182 + <div class="field" x-show="bot.group_mode !== 'off'">
183 + <div class="field-label">
184 + <div class="field-title">Welcome New Members</div>
185 + <div class="field-description">Send a greeting when someone joins the group</div>
186 + </div>
187 + <div class="field-control">
188 + <label class="toggle">
189 + <input type="checkbox" x-model="bot.welcome_enabled" />
190 + <span class="toggler"></span>
191 + </label>
192 + </div>
193 + </div>
194 +
195 + <div class="field" x-show="bot.group_mode !== 'off' && bot.welcome_enabled">
196 + <div class="field-label">
197 + <div class="field-title">Welcome Message</div>
198 + <div class="field-description">Use {name} for the new member's name</div>
199 + </div>
200 + <div class="field-control">
201 + <input type="text" x-model="bot.welcome_message" placeholder="Welcome, {name}!" />
202 + </div>
203 + </div>
204 +
205 <div class="field">
206 <div class="field-label">
207 <div class="field-title">User Project Mapping</div>
plugins/_telegram_integration/webui/telegram-config-store.js
+2
@@ -32,6 +32,8 @@ export const store = createStore("telegramConfig", {
32 webhook_secret: "",
33 allowed_users: [],
34 group_mode: "mention",
35 + welcome_enabled: false,
36 + welcome_message: "",
37 user_projects: {},
38 default_project: "",
39 agent_instructions: "",