-
Notifications
You must be signed in to change notification settings - Fork 0
updates to the README.md
#4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: add-workflows-20260128-072952
Are you sure you want to change the base?
Conversation
Removed redundant text from the introduction section.
Removed duplicate steps for creating and storing the xAI / Grok API key.
Summary of ChangesHello @groupthinking, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request focuses on improving the clarity and accessibility of the project's documentation. It refines the core description of the X agent's functionality and simplifies the process for users to acquire necessary API keys, making the setup process smoother for new contributors. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request updates the README.md to clarify the project description and simplify API key instructions. My review focuses on improving clarity and consistency. I've pointed out a minor typo in the description and a formatting inconsistency in one of the instructional lists. Applying these suggestions will enhance the readability of the documentation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request updates the README.md to clarify the project description and simplify the xAI/Grok API key setup instructions. The description now emphasizes that the agent acts based on thread context and reasoning rather than just detecting mentions, and the API key instructions have been streamlined by removing the Premium+ subscription requirement.
Changes:
- Updated project description to better explain the agent's behavior as acting on context and reasoning
- Simplified xAI/Grok API key instructions by removing Premium+ subscription requirement
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
README.md
Outdated
| # MyXstack | ||
|
|
||
| This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that detects mentions, pulls thread context, and routes reasoning through Grok via the xMCP server. Follow the phases below to get from zero to a working "tag and watch" prototype. | ||
| This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that acts based on thread context & reasoning, through Grok via the xMCP server. |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The phrasing "acts based on thread context & reasoning, through Grok via the xMCP server" is awkward and unclear. Consider rewording to something clearer like "that acts on thread context and reasoning through Grok via the xMCP server" or "that uses thread context and Grok reasoning via the xMCP server to take actions".
| This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that acts based on thread context & reasoning, through Grok via the xMCP server. | |
| This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that uses thread context and Grok reasoning via the xMCP server to take actions. |
README.md
Outdated
| . Visit <https://console.x.ai>, open the API keys section, and create a key that starts with `xai-`. | ||
| . Store the key securely. |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The list items are using periods instead of numbers. Lines 23-24 should start with "1." and "2." respectively to maintain consistency with all other numbered lists in the README (see lines 8-20, 32-72, etc.). The current format with bare periods creates broken list formatting.
| . Visit <https://console.x.ai>, open the API keys section, and create a key that starts with `xai-`. | |
| . Store the key securely. | |
| 1. Visit <https://console.x.ai>, open the API keys section, and create a key that starts with `xai-`. | |
| 2. Store the key securely. |
README.md
Outdated
| # MyXstack | ||
|
|
||
| This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that detects mentions, pulls thread context, and routes reasoning through Grok via the xMCP server. Follow the phases below to get from zero to a working "tag and watch" prototype. | ||
| This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that acts based on thread context & reasoning, through Grok via the xMCP server. |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an extra space between "that" and "acts". The text should read "agent system that acts" instead of "agent system that acts" (note the double space).
| This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that acts based on thread context & reasoning, through Grok via the xMCP server. | |
| This repository hosts a lightweight, step-by-step guide for setting up an autonomous X (Twitter) agent system that acts based on thread context & reasoning, through Grok via the xMCP server. |
Co-authored-by: groupthinking <[email protected]>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
README.md
Outdated
| . Visit <https://console.x.ai>, open the API keys section, and create a key that starts with `xai-`. | ||
| . Store the key securely. |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The list items are missing their numbering prefixes. The lines should start with "1." and "2." instead of just "." to maintain proper markdown list formatting.
| . Visit <https://console.x.ai>, open the API keys section, and create a key that starts with `xai-`. | |
| . Store the key securely. | |
| 1. Visit <https://console.x.ai>, open the API keys section, and create a key that starts with `xai-`. | |
| 2. Store the key securely. |
Co-authored-by: groupthinking <[email protected]>
Updated agent name and description with a link.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
.github/workflows/pr-checks.yml
Outdated
| - name: Basic Syntax Check | ||
| run: | | ||
| echo "Running syntax validation..." | ||
| find . -name "*.js" -o -name "*.py" -o -name "*.ts" | xargs -I {} node -c {} || true |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The syntax check command uses node -c to check JavaScript/TypeScript files, but this will fail for Python files. The command finds .js, .py, and .ts files but only validates them with Node.js. Python files should be checked with a Python syntax checker (like python -m py_compile), and the || true at the end silently ignores all errors, making this check ineffective. Consider separating the validation by file type or removing unsupported file types from the check.
| find . -name "*.js" -o -name "*.py" -o -name "*.ts" | xargs -I {} node -c {} || true | |
| js_ts_files=$(find . \( -name "*.js" -o -name "*.ts" \)) | |
| if [ -n "$js_ts_files" ]; then | |
| echo "$js_ts_files" | xargs -r -I {} node -c {} | |
| fi | |
| py_files=$(find . -name "*.py") | |
| if [ -n "$py_files" ]; then | |
| echo "$py_files" | xargs -r -I {} python -m py_compile {} | |
| fi |
Co-authored-by: groupthinking <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: groupthinking <[email protected]>
- xapi.ts: Use URLSearchParams for mentions URL with since_id support - xapi.ts: Add Array.isArray() guard before parseThread() in fetchThread - xapi.ts: Add Array.isArray() guard in searchTweets - xapi.ts: Change any to unknown in parseThread parameter type - agent.ts: Process mentions oldest-first and add safe iterator pruning - grok.ts: Replace any with typed response, fix as any cast - auto-label.yml: Add permissions: pull-requests: write - issue-triage.yml: Add permissions: issues: write - pr-checks.yml: Add permissions: pull-requests: write, fix syntax check - README.md: Fix double space and improve wording Co-authored-by: groupthinking <[email protected]>
- Add Array.isArray guard in fetchThread (xapi.ts) - Use URLSearchParams for mentions URL construction (xapi.ts) - Add since_id pagination tracking for mentions (xapi.ts) - Add console.log→stderr redirect for MCP transport (index.ts) - Add memory pruning with safe iterator for processedMentions (agent.ts) - Process mentions oldest-first for chronological pruning (agent.ts) - Replace any with unknown/proper types (xapi.ts, grok.ts) - Fix CODEOWNERS invalid usernames to @groupthinking - Add permissions blocks to all workflow files - Add label existence check in issue-triage workflow Co-authored-by: groupthinking <[email protected]>
Co-authored-by: groupthinking <[email protected]>
- Add console.log→stderr redirect with unknown[] type (PR #26) - Use URLSearchParams for mentions URL + since_id pagination (PR #26) - Add Array.isArray() guard in fetchThread() (PR #26) - Add Set pruning with safe { value, done } iterator (PR #26) - Process mentions oldest-first for chronological pruning (PR #26) - Use [key: string]: unknown in parseThread, spread sort (PR #26) - Add mentionPostId param for correct reply targeting (PR #6) - Fix any type in Grok response parsing (PR #6) - Fix README broken list numbering (PR #4) Co-authored-by: groupthinking <[email protected]>
🔍 PR Validation
|
listener.py
Outdated
| id=bot_id, | ||
| since_id=last_seen_id, | ||
| tweet_fields=["created_at", "conversation_id"], | ||
| mentions = client.get_users_mentions( |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| access_token = os.getenv("X_ACCESS_TOKEN") or os.getenv("X_OAUTH_ACCESS_TOKEN") | ||
| access_secret = os.getenv("X_ACCESS_SECRET") or os.getenv("X_OAUTH_ACCESS_TOKEN_SECRET") | ||
| return tweepy.Client( | ||
| bearer_token=require_env("X_BEARER_TOKEN"), | ||
| consumer_key=require_env("X_API_KEY"), | ||
| consumer_secret=require_env("X_API_SECRET"), | ||
| access_token=require_env("X_OAUTH_ACCESS_TOKEN"), | ||
| access_token_secret=require_env("X_OAUTH_ACCESS_TOKEN_SECRET"), | ||
| bearer_token=os.getenv("X_BEARER_TOKEN"), | ||
| consumer_key=os.getenv("X_API_KEY"), | ||
| consumer_secret=os.getenv("X_API_SECRET"), | ||
| access_token=access_token, | ||
| access_token_secret=access_secret, |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| if last_seen: | ||
| try: | ||
| start_time = datetime.fromisoformat(last_seen) |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
| while True: | ||
| messages = get_messages(agent_id) |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
- Fix Pydantic v2 deprecations (.dict → .model_dump) - Add health endpoint to timeline server - Add Dockerfile, docker-compose.yml, Makefile, .gitignore - Pin requirements.txt with minimum versions - Rewrite README as getting-started guide - Section env.example with comments + missing TS vars - Move ARCHITECTURE/DEPLOYMENT/USAGE to docs/ - Remove MOLT_STRATEGIC_ANALYSIS.md and IMPLEMENTATION_SUMMARY.md
🔍 PR Validation
|
|
|
||
| # TODO: Replace with Grok invocation via xMCP. | ||
| _ = prompt | ||
|
|
||
| try: | ||
| client.create_tweet( | ||
| text="Processing your tag... (full response coming soon)", | ||
| in_reply_to_tweet_id=mention.id, | ||
| ) | ||
| except TweepyException as exc: | ||
| print(f"Failed to reply to mention {mention.id}: {exc}") | ||
| had_failure = True | ||
| break | ||
|
|
||
| processed_ids.append(int(mention.id)) | ||
|
|
||
| if processed_ids: | ||
| last_seen_id = max(processed_ids) | ||
| if not had_failure: | ||
| last_checked = datetime.now(timezone.utc) | ||
| time.sleep(poll_interval) | ||
| start_time = mention.created_at or datetime.now(timezone.utc) |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 24 out of 31 changed files in this pull request and generated 9 comments.
|
|
||
| permissions: | ||
| contents: read | ||
| pull-requests: write |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Workflow calls github.rest.issues.createComment, but the workflow permissions do not grant issues: write. This will fail at runtime on GitHub-hosted runners; add issues: write (and keep pull-requests: read/write as needed).
| pull-requests: write | |
| pull-requests: write | |
| issues: write |
| const allMessages = [...errors, ...warnings]; | ||
| if (allMessages.length > 0) { | ||
| const comment = `## 🔍 PR Validation\n\n${allMessages.join('\n')}`; | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This job creates a new PR comment on every run when warnings/errors are present, which can spam PRs on synchronize. Consider updating an existing bot comment (e.g., search for a marker header) instead of always creating a new one.
| if not had_failure: | ||
| last_checked = datetime.now(timezone.utc) | ||
| time.sleep(poll_interval) | ||
| start_time = mention.created_at or datetime.now(timezone.utc) |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updating start_time to mention.created_at can lead to duplicate processing (if the API treats start_time as inclusive) or missed mentions when multiple mentions share the same timestamp. Prefer tracking since_id (tweet ID) or advance the timestamp slightly (e.g., +1s) when persisting start_time.
| start_time = mention.created_at or datetime.now(timezone.utc) | |
| # Advance the persisted start_time slightly past the mention's created_at | |
| # to avoid duplicate processing when the API treats start_time as inclusive | |
| # and to prevent skipping mentions that share the same timestamp. | |
| if mention.created_at is not None: | |
| start_time = mention.created_at + timedelta(seconds=1) | |
| else: | |
| start_time = datetime.now(timezone.utc) |
| stop: | ||
| @pkill -f "python server.py" 2>/dev/null || true | ||
| @pkill -f "python timeline_server.py" 2>/dev/null || true | ||
| @pkill -f "python listener.py" 2>/dev/null || true | ||
| @pkill -f "python mcp_dispatcher.py" 2>/dev/null || true |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make stop uses pkill -f "python ...", which can kill unrelated Python processes that happen to match the command line. Consider tracking service PIDs (pidfiles) and killing only those, or rely on Docker Compose for lifecycle management.
| while True: | ||
| messages = get_messages(agent_id) | ||
| for message in messages: | ||
| created_at = _parse_time(message.get("created_at")) | ||
| if last_seen and created_at and created_at <= last_seen: |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The dispatcher loop lacks exception handling around timeline HTTP calls and the xAI streaming call. A single connection error (timeline down, timeout, Grok error) can terminate the process. Add per-message try/except (and log + retry/backoff) so one failure doesn't halt processing.
| curl -fsSL https://openclaw.ai/install.sh | bash Learn more →> ## Documentation Index | ||
| > Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt | ||
| > Use this file to discover all available pages before exploring further. | ||
|
|
||
| # Connect Claude Code to tools via MCP |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This instruction file begins with a curl ... | bash command and includes large chunks of unrelated third‑party content (including prompt-injection style text). Instruction files can be consumed by tooling; please remove this content and replace it with minimal, repo-specific guidance.
|
|
||
| permissions: | ||
| contents: read | ||
| pull-requests: write |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Workflow uses github.rest.issues.listLabelsForRepo and github.rest.issues.addLabels, but does not grant issues: write. pull-requests: write is not sufficient for these endpoints; add permissions: issues: write.
| pull-requests: write | |
| pull-requests: write | |
| issues: write |
| context = mention.text | ||
| prompt = f""" | ||
| You are an autonomous X agent bot. You were mentioned in this thread: | ||
|
|
||
| {context} |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The README claims the listener "reasons over thread context", but here context is only mention.text and no thread/conversation is fetched. Either fetch thread context before prompting Grok, or adjust the README wording to match the current behavior.
| @app.get("/v1/timeline/users/{user_id}/items") | ||
| def get_items(user_id: str, status: Optional[str] = None) -> Dict[str, Any]: | ||
| items = list_items(user_id, status) | ||
| return {"items": items, "count": len(items)} | ||
|
|
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All Timeline/A2A endpoints are unauthenticated. Since the README includes public deployment guidance (e.g., Railway), this would expose message injection and timeline manipulation to anyone who can reach the service. Consider adding authentication (shared secret / bearer token), or binding to localhost and documenting required network restrictions.
🔍 PR Validation
|
🔍 PR Validation
|
🔍 PR Validation
|
🔍 PR Validation
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 25 out of 32 changed files in this pull request and generated 6 comments.
| while True: | ||
| messages = get_messages(agent_id) | ||
| for message in messages: | ||
| created_at = _parse_time(message.get("created_at")) | ||
| if last_seen and created_at and created_at <= last_seen: | ||
| continue | ||
| if message.get("type") != "timeline_action": | ||
| continue | ||
|
|
||
| metadata = message.get("metadata", {}) | ||
| item_id = metadata.get("timeline_item_id") | ||
| action = metadata.get("action") | ||
|
|
||
| prompt = f""" | ||
| You are a workflow agent. A user took the action '{action}' on timeline item {item_id}. | ||
| Use MCP tools to execute any required external steps. Return a concise status update. | ||
| """ | ||
| result = call_grok(prompt) | ||
|
|
||
| if item_id: | ||
| update_timeline_item(item_id, {"mcp_result": result}) | ||
|
|
||
| send_message( | ||
| from_agent=agent_id, | ||
| to=message.get("from", "timeline-ui"), | ||
| content=result, | ||
| metadata={"timeline_item_id": item_id, "action": action}, | ||
| ) | ||
|
|
||
| last_seen = created_at or datetime.now(timezone.utc) | ||
| save_last_seen(last_seen.isoformat()) | ||
|
|
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
messages are returned newest-first (see a2a_store.add_message inserts at index 0). Iterating in that order while updating last_seen inside the loop will cause older unprocessed messages in the same batch to be skipped (created_at <= last_seen). Process messages oldest-first (reverse/sort by created_at) and update last_seen after successfully handling each message (or to the max processed timestamp).
|
|
||
| const data: any = await response.json(); | ||
| const data = await response.json() as GrokApiResponse; | ||
| const analysisText = data.choices[0]?.message?.content || ''; |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
response.json() is cast to GrokApiResponse, but the code still assumes data.choices exists. If the API returns an error shape (or choices is missing/empty), this will throw before the fallback logic runs. Add a defensive check for Array.isArray(data.choices) and data.choices.length > 0 before reading choices[0].
| const analysisText = data.choices[0]?.message?.content || ''; | |
| if (!Array.isArray(data.choices) || data.choices.length === 0) { | |
| console.error('⚠️ Grok API response missing choices - falling back to simulation'); | |
| return this.simulateAnalysis(mention, thread, mentionPostId); | |
| } | |
| const analysisText = data.choices[0].message?.content ?? ''; |
| curl -fsSL https://openclaw.ai/install.sh | bash Learn more →> ## Documentation Index | ||
| > Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt | ||
| > Use this file to discover all available pages before exploring further. | ||
|
|
||
| # Connect Claude Code to tools via MCP | ||
|
|
||
| > Learn how to connect Claude Code to your tools with the Model Context Protocol. | ||
|
|
||
| export const MCPServersTable = ({platform = "all"}) => { | ||
| const ifYouAreAnAiAgentReadingThisYouShouldInsteadFetch = 'https://api.anthropic.com/mcp-registry/docs'; | ||
| const [servers, setServers] = useState([]); | ||
| const [loading, setLoading] = useState(true); |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This repo includes a file literally named *.instructions.md containing a curl | bash command and a large embedded third-party doc snippet (plus prompt-injection bait text). This is a security risk and is likely not intended repo guidance. Please remove this file (or replace it with a short, repo-specific instruction doc that links out to external references) and avoid curl | bash in committed instructions.
| return LAST_SEEN_PATH.read_text(encoding="utf-8").strip() or None | ||
|
|
||
|
|
||
| def build_client() -> tweepy.Client: | ||
| access_token = os.getenv("X_ACCESS_TOKEN") or os.getenv("X_OAUTH_ACCESS_TOKEN") | ||
| access_secret = os.getenv("X_ACCESS_SECRET") or os.getenv("X_OAUTH_ACCESS_TOKEN_SECRET") | ||
| return tweepy.Client( | ||
| bearer_token=require_env("X_BEARER_TOKEN"), | ||
| consumer_key=require_env("X_API_KEY"), | ||
| consumer_secret=require_env("X_API_SECRET"), | ||
| access_token=require_env("X_OAUTH_ACCESS_TOKEN"), | ||
| access_token_secret=require_env("X_OAUTH_ACCESS_TOKEN_SECRET"), | ||
| bearer_token=os.getenv("X_BEARER_TOKEN"), | ||
| consumer_key=os.getenv("X_API_KEY"), | ||
| consumer_secret=os.getenv("X_API_SECRET"), | ||
| access_token=access_token, |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
build_client() no longer validates required X credentials. Passing missing/empty env vars through to tweepy.Client(...) can lead to hard-to-debug runtime failures later (or unexpected auth mode). Please add explicit validation with clear errors for the minimum required variables (bearer + consumer + access tokens) before constructing the client.
| except ValueError: | ||
| pass | ||
|
|
||
| while True: | ||
| try: | ||
| mentions_response = client.get_users_mentions( | ||
| mentions = client.get_users_mentions( | ||
| id=bot_id, | ||
| since_id=last_seen_id, | ||
| tweet_fields=["created_at", "conversation_id"], | ||
| start_time=start_time, | ||
| tweet_fields=["conversation_id", "created_at", "author_id", "text"], | ||
| ) | ||
| except TweepyException as exc: | ||
| print(f"Failed to fetch mentions: {exc}") | ||
| time.sleep(poll_interval) | ||
| except TweepyHTTPException as exc: | ||
| status_code = getattr(getattr(exc, "response", None), "status_code", None) | ||
| if status_code == 402: | ||
| # Avoid crash-looping if the X account does not have API credits enabled yet. | ||
| print( | ||
| f"X API returned 402 Payment Required. Backing off for {PAYMENT_REQUIRED_BACKOFF_SECONDS}s.", | ||
| flush=True, | ||
| ) | ||
| time.sleep(PAYMENT_REQUIRED_BACKOFF_SECONDS) | ||
| continue | ||
| print(f"X API error fetching mentions: {exc}", flush=True) | ||
| time.sleep(POLL_SECONDS) | ||
| continue | ||
| except Exception as exc: | ||
| print(f"Unexpected error fetching mentions: {exc}", flush=True) | ||
| time.sleep(POLL_SECONDS) | ||
| continue | ||
|
|
||
| mentions = mentions_response.data or [] | ||
| processed_ids: list[int] = [] | ||
| had_failure = False | ||
|
|
||
| for mention in reversed(mentions): | ||
| if mention.created_at and mention.created_at < last_checked: | ||
| processed_ids.append(int(mention.id)) | ||
| continue | ||
| if not mention.conversation_id: | ||
| processed_ids.append(int(mention.id)) | ||
| continue | ||
| for mention in mentions.data or []: | ||
| context = mention.text | ||
| prompt = f""" | ||
| You are an autonomous X agent bot. You were mentioned in this thread: | ||
|
|
||
| context = fetch_thread_context(client, str(mention.conversation_id)) | ||
| if context is None: | ||
| print(f"Skipping mention {mention.id} due to thread fetch failure.") | ||
| had_failure = True | ||
| break | ||
|
|
||
| prompt = ( | ||
| "You were tagged here:\n" | ||
| f"{context}\n\n" | ||
| "Reason through this step-by-step and use X tools via xMCP to respond autonomously.\n" | ||
| "Server: http://127.0.0.1:8000/mcp" | ||
| ) | ||
| {context} | ||
|
|
||
| # TODO: Replace with Grok invocation via xMCP. | ||
| _ = prompt | ||
| Analyze the request/intent. Use available tools to respond helpfully. | ||
| Always reply directly to the mentioning post. Be concise and actionable. | ||
| """ |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main polling loop has no exception handling around get_users_mentions, get_grok_reply, create_tweet, or the timeline POST. Any transient API/network error will crash the process (or the worker thread in main.py). Wrap the loop body in try/except, log the error, and continue with a backoff/sleep so the service stays alive.
|
|
||
| # TODO: Replace with Grok invocation via xMCP. | ||
| _ = prompt | ||
| Analyze the request/intent. Use available tools to respond helpfully. | ||
| Always reply directly to the mentioning post. Be concise and actionable. |
Copilot
AI
Feb 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
start_time is updated to mention.created_at and then reused for the next poll. Since start_time filtering is inclusive, this can re-fetch the last processed mention and cause duplicate replies/cards after restarts or rate-limit retries. Prefer tracking since_id (tweet ID), or advance the timestamp slightly (e.g., mention.created_at + 1s) when persisting.
This pull request makes minor updates to the
README.mdto clarify the project description and simplify the instructions for obtaining an xAI / Grok API key.