From 160875f3be0add2870048986c06b5b4e38e99874 Mon Sep 17 00:00:00 2001 From: konard Date: Fri, 12 Sep 2025 22:15:31 +0300 Subject: [PATCH 1/4] Initial commit with task details for issue #150 Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/linksplatform/Bot/issues/150 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..9d133528 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/linksplatform/Bot/issues/150 +Your prepared branch: issue-150-f759718f +Your prepared working directory: /tmp/gh-issue-solver-1757704527074 + +Proceed. \ No newline at end of file From da1f45dceda040d9e917d6b2b55cf218863dd34c Mon Sep 17 00:00:00 2001 From: konard Date: Fri, 12 Sep 2025 22:21:33 +0300 Subject: [PATCH 2/4] Implement VK Bot daily outreach for programming languages and GitHub profiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add DailyOutreach module to ask random users about programming languages and GitHub profiles once daily - Automatically process user responses and add information to their profiles - Integration with existing VK bot architecture in __main__.py - Support for detecting GitHub links and programming language mentions in responses - Daily frequency control to prevent spam 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- python/__main__.py | 16 +++++ python/modules/__init__.py | 1 + python/modules/daily_outreach.py | 120 +++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 python/modules/daily_outreach.py diff --git a/python/__main__.py b/python/__main__.py index cdcbf7f6..d78066a8 100644 --- a/python/__main__.py +++ b/python/__main__.py @@ -10,6 +10,7 @@ from modules import ( BetterBotBaseDataService, Commands ) +from modules.daily_outreach import DailyOutreach from tokens import BOT_TOKEN from userbot import UserBot import patterns @@ -38,6 +39,7 @@ def __init__( self.messages_to_delete = {} self.userbot = UserBot() self.data = BetterBotBaseDataService() + self.daily_outreach = DailyOutreach(self, self.data) self.commands = Commands(self, self.data) self.commands.register_cmds( (patterns.HELP, self.commands.help_message), @@ -99,6 +101,20 @@ def message_new( user = self.data.get_user(from_id, self) if from_id > 0 else None + # Daily outreach: ask random user about programming languages or GitHub + if peer_id >= 2e9 and self.daily_outreach.should_run_daily_outreach(): # Only in group chats + try: + self.daily_outreach.send_daily_question(peer_id) + except Exception as e: + print(f"Daily outreach error: {e}") + + # Process potential responses to daily questions + if from_id > 0 and user: + try: + self.daily_outreach.process_potential_response(msg, from_id) + except Exception as e: + print(f"Response processing error: {e}") + messages = self.get_messages(event) selected_message = messages[0] if len(messages) == 1 else None selected_user = ( diff --git a/python/modules/__init__.py b/python/modules/__init__.py index 6f0b661e..b1a54d4f 100644 --- a/python/modules/__init__.py +++ b/python/modules/__init__.py @@ -4,6 +4,7 @@ from .data_service import BetterBotBaseDataService from .data_builder import DataBuilder from .vk_instance import VkInstance +from .daily_outreach import DailyOutreach from .utils import ( get_default_programming_language, contains_string, diff --git a/python/modules/daily_outreach.py b/python/modules/daily_outreach.py new file mode 100644 index 00000000..2f2205cf --- /dev/null +++ b/python/modules/daily_outreach.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +"""Daily outreach module for asking users about programming languages and GitHub profiles.""" +import random +from datetime import datetime, timedelta +from typing import List, NoReturn, Optional +from regex import search, IGNORECASE + +from .data_service import BetterBotBaseDataService +import sys +import os +sys.path.append(os.path.dirname(os.path.dirname(__file__))) +import config + + +class DailyOutreach: + """Handles daily outreach to users without programming languages or GitHub profiles.""" + + QUESTIONS = [ + "У вас есть страничка на GitHub?", + "Какие языки программирования вы знаете?" + ] + + def __init__(self, vk_instance, data_service: BetterBotBaseDataService): + self.vk_instance = vk_instance + self.data_service = data_service + self.last_outreach_date = None + + def should_run_daily_outreach(self) -> bool: + """Check if daily outreach should run (once per day).""" + today = datetime.now().date() + if self.last_outreach_date != today: + self.last_outreach_date = today + return True + return False + + def find_users_without_profile_info(self, peer_id: int) -> List[int]: + """Find users without programming languages or GitHub profiles.""" + try: + member_ids = self.vk_instance.get_members_ids(peer_id) + if not member_ids: + return [] + + candidates = [] + for uid in member_ids: + user = self.data_service.get_user(uid, self.vk_instance) + + # Check if user has no programming languages and no GitHub profile + has_languages = (hasattr(user, 'programming_languages') and + user.programming_languages and + len(user.programming_languages) > 0) + has_github = (hasattr(user, 'github_profile') and + user.github_profile and + user.github_profile.strip() != "") + + if not has_languages and not has_github: + candidates.append(uid) + + return candidates + except Exception as e: + print(f"Error finding users: {e}") + return [] + + def select_random_user_and_question(self, candidates: List[int]) -> Optional[tuple]: + """Select a random user and question.""" + if not candidates: + return None + + user_id = random.choice(candidates) + question = random.choice(self.QUESTIONS) + return user_id, question + + def send_daily_question(self, peer_id: int) -> NoReturn: + """Send daily question to a random user.""" + candidates = self.find_users_without_profile_info(peer_id) + + if not candidates: + return + + selection = self.select_random_user_and_question(candidates) + if not selection: + return + + user_id, question = selection + try: + user_name = self.vk_instance.get_user_name(user_id, "nom") + message = f"[id{user_id}|{user_name}], {question}" + self.vk_instance.send_msg(message, peer_id) + except Exception as e: + print(f"Error sending daily question: {e}") + + def process_potential_response(self, msg: str, from_id: int) -> bool: + """ + Process a message that might be a response to our daily questions. + Returns True if response was processed, False otherwise. + """ + msg_lower = msg.lower() + + # Check for GitHub profile response + github_match = search(r'github\.com/([a-zA-Z0-9-_]+)', msg, IGNORECASE) + if github_match: + username = github_match.group(1) + user = self.data_service.get_user(from_id, self.vk_instance) + user.github_profile = username + self.data_service.save_user(user) + return True + + # Check for programming language response + for lang in config.DEFAULT_PROGRAMMING_LANGUAGES: + # Remove regex escaping for matching + lang_clean = lang.replace(r'\+', '+').replace(r'\-', '-').replace(r'\\', '') + if search(lang_clean, msg, IGNORECASE): + user = self.data_service.get_user(from_id, self.vk_instance) + if not hasattr(user, 'programming_languages'): + user.programming_languages = [] + if lang_clean not in user.programming_languages: + user.programming_languages.append(lang_clean) + self.data_service.save_user(user) + return True + + return False \ No newline at end of file From 1b4d3094933f473ac89d976ef1bb26d3909d7f16 Mon Sep 17 00:00:00 2001 From: konard Date: Fri, 12 Sep 2025 22:22:24 +0300 Subject: [PATCH 3/4] 'Auto-commit changes made by Claude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude ' --- examples/test_daily_outreach.py | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 examples/test_daily_outreach.py diff --git a/examples/test_daily_outreach.py b/examples/test_daily_outreach.py new file mode 100644 index 00000000..7294d211 --- /dev/null +++ b/examples/test_daily_outreach.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""Test script for daily outreach functionality.""" +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'python')) + +from modules.daily_outreach import DailyOutreach +from modules.data_service import BetterBotBaseDataService +from modules.vk_instance import VkInstance + +def test_daily_outreach(): + """Test the daily outreach functionality.""" + print("Testing DailyOutreach functionality...") + + # Create mock VK instance and data service + vk_instance = VkInstance() + data_service = BetterBotBaseDataService("test_users") + + # Create daily outreach instance + daily_outreach = DailyOutreach(vk_instance, data_service) + + print("✓ DailyOutreach instance created successfully") + + # Test question selection + candidates = [12345, 67890] + selection = daily_outreach.select_random_user_and_question(candidates) + if selection: + user_id, question = selection + print(f"✓ Random selection works: User {user_id}, Question: '{question}'") + else: + print("✗ Random selection failed") + + # Test response processing + test_responses = [ + ("My GitHub is github.com/testuser", 12345), + ("I know Python and JavaScript", 67890), + ("Regular message", 11111) + ] + + for msg, from_id in test_responses: + processed = daily_outreach.process_potential_response(msg, from_id) + print(f"✓ Message '{msg}' processed: {processed}") + + # Test should_run_daily_outreach + should_run_first = daily_outreach.should_run_daily_outreach() + should_run_second = daily_outreach.should_run_daily_outreach() + + print(f"✓ Daily check - First run: {should_run_first}, Second run: {should_run_second}") + + print("✓ All tests completed successfully!") + +if __name__ == "__main__": + test_daily_outreach() \ No newline at end of file From 228e39d431bd98a7421d5d5d02b2d0e4b2360c78 Mon Sep 17 00:00:00 2001 From: konard Date: Fri, 12 Sep 2025 22:22:26 +0300 Subject: [PATCH 4/4] Remove CLAUDE.md - Claude command completed --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 9d133528..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/linksplatform/Bot/issues/150 -Your prepared branch: issue-150-f759718f -Your prepared working directory: /tmp/gh-issue-solver-1757704527074 - -Proceed. \ No newline at end of file