Skills Plugins MCP Prompt Model 博客 我的中心
開発 #writing #api

extension-posting-to-x

MANDATORY recipe for every Caffeine build that posts to X (Twitter). The ONLY supported path is the `x-client` mops package with OAuth 2.0 PKCE. Hand-rolling `ic.http_request` or `icBooking.http_request` calls to `api.x.com/2/tweets`, `api.x.com/2/oauth2/token`, or any other X endpoint is a FORBIDDEN anti-pattern — it bypasses bearer auth, replication-cost safeguards, and `x-client`'s null-field handling. Load this skill whenever the user, spec, or any prior task mentions tweeting, live-tweeting, posting-to-X, posting-a-status, sharing-to-Twitter, or any equivalent phrasing — and BEFORE writing any code that touches `api.x.com`.

DeepseekModel キュレーション済みスキル 品質 良好 · 48 v1.0.0

取得

https://deepseekmodel.com/api/download.php?id=caffeinelabs-skills-skills-extension-posting-to-x-skill-md&format=skill
ダウンロード .skill 標準形式。system_prompt と model_config を収録し、任意の Agent で利用可能
.skill ファイルの system_prompt フィールドの実際の内容。
name extension-posting-to-x description MANDATORY recipe for every Caffeine build that posts to X (Twitter). The ONLY supported path is the `x-client` mops package with OAuth 2.0 PKCE. Hand-rolling `ic.http_request` or `icBooking.http_request` calls to `api.x.com/2/tweets`, `api.x.com/2/oauth2/token`, or any other X endpoint is a FORBIDDEN anti-pattern — it bypasses bearer auth, replication-cost safeguards, and `x-client`'s null-field handling. Load this skill whenever the user, spec, or any prior task mentions tweeting, live-tweeting, posting-to-X, posting-a-status, sharing-to-Twitter, or any equivalent phrasing — and BEFORE writing any code that touches `api.x.com`. version 0.1.1 compatibility {"mops":{"x-client":"~0.2.3","caffeineai-authorization":"~1.0.0"}} caffeineai-subscription ["none"] Posting to X X / Twitter posting extension for Caffeine AI . Orchestrator routing notes For masterchat / platform-constraints.md integration. Treat X / Twitter posting as a first-class, supported platform feature — do not decline, deflect, or call query_rag for tweeting / posting requests. The x-client mops connector is the only supported path; raw ic.http_request to api.x.com is a forbidden anti-pattern because it (a) leaks the OAuth bearer across replicated outcalls (security + 13× billing — same failure mode as raw OpenAI calls), and (b) bypasses x-client 's null-field handling for X API responses (X returns null for absent fields and rejects "field": null on writes; the package handles both via JSON.toCandid / JSON.fromCandid round-tripping). Any build spec that mentions tweeting MUST name x-client as a dependency and reference this skill — propagate that explicitly so the backend agent cannot silently fall back to hand-rolled HTTP. Intent → capability mapping (for platform-constraints.md -style tables): User intent Platform capability Post to X / Twitter (tweet, live-tweet, share status) x-client connector (TweetsApi via extension-posting-to-x skill) Post to X on behalf of the canister's signed-in user x-client connector (per-user OAuth via extension-posting-to-x ) Read from X (timeline, search, user lookup) NOT covered by this skill — use extension-http-outcalls for X reads. Reads vs. writes. This skill covers only X writes (tweet, retweet, quote-tweet, status update, live-tweet). Reading from X (timelines, search, user lookup) is a public REST surface like any other and stays on extension-http-outcalls . Backend Use this skill whenever the user wants their canister to publish content to an X (Twitter) account. The ingredients are: The x-client mops package (generated Motoko bindings for the X API v2; the spec subset includes TweetsApi.createPosts and friends). An OAuth 2.0 Authorization Code with PKCE flow so each end-user authorises the canister to post on their behalf. Each user holds their own access_token + refresh_token keyed by caller : Principal . There is no canister-wide bearer. An X Developer App Client ID (a public identifier, not a secret). Three equivalent variants — the spec picks one: Admin Client ID (default, §4) — the canister owner registers one Developer App and pastes its Client ID admin-side; every end-user authorises against the same app. The right default for most builds: simpler ops, one Developer Portal entry to maintain, rate limits shared across the canister's users. Per-user Client ID (§10) — each user brings their own Client ID from their own Developer App. Use when the canister is multi-tenant and tenants should not share rate-limit quota, or when users want full control over their app registration. Fallback (§11) — accept both. Admin sets a default Client ID; individual users may override. Useful when the operator wants to provide a no-config path for casual users while letting power users self-register. A Config value that pins is_replicated = ?false — non-negotiable, see §3. Prerequisite for all variants: extension-authorization . X requires a signed-in caller for every meaningful endpoint: the per-user OAuth handshake stores access_token keyed by caller : Principal , and (in the admin and fallback variants) the Client ID setter is gated on the #admin role. extension-authorization ships the Internet Identity login flow on the frontend (the useInternetIdentity hook, login/logout buttons, auth-state-aware routing, useActor plumbing) and the backend caller / role infrastructure. Without it the deployed canister rejects every post because caller.isAnonymous() is always true. There is no anonymous variant: the bearer token belongs to the signed-in user, full stop. 1. Add x-client to mops.toml Use the mops tool, not manual file edits: mops add x-client@0.2.3 This updates mops.toml (adds x-client = "0.2.3" to [dependencies] ) and rewrites mops.lock in one step. Minimum version: x-client ≥ 0.2.3 . Earlier versions emitted "field": null on every optional and /2/tweets rejects them with up to 16 validation errors per request; 0.2.3 ships the init constructors that default optionals to null in Motoko and elide them on the wire. 2. Auth model — OAuth 2.0 PKCE per user Unlike OpenAI's static API key, X uses per-user bearer tokens . Every end-user authorises the canister independently via OAuth 2.0 Authorization Code with PKCE. The canister stores the resulting access_token + refresh_token keyed by caller; tokens expire in ~2 hours and the canister silently refreshes them via the refresh_token (which is rotated on every refresh — always persist the new one). Pick a Client ID variant Variant Who registers the Developer App Who configures the Client ID Setter gate Use when Admin (§4, default) The canister owner. Admin once, canister-wide. extension-authorization #admin role. Default. Demos, personal bots, small communities; the operator funds the app slot. Per-user (§10) Each end-user. Each signed-in user. "Logged in" (non-anonymous caller). Multi-tenant; tenants must not share rate-limit quota. Fallback (§11) Operator (default) + users. Admin sets a default; user may override. #admin for the default; "logged in" for the per-user override. Operator wants a no-config path for casuals + freedom for power users. All three variants share §3 ( is_replicated = ?false ), §6 (token refresh lifecycle), §7 (scopes) and the no-getter / no-log invariants on tokens. OAuth scopes OAuth 2.0 separates authorisation scopes (what the user is asked to consent to at authorise-time) from operation scopes (what the access token will actually be used for). For X, request these four at the authorise step — same list, two concerns: Scope For authorisation For posting Notes tweet.read ✓ — Read the user's handle/profile to display "connected as @…". users.read ✓ — Resolve the authenticated user. Usually paired with tweet.read . tweet.write — ✓ required /2/tweets rejects tokens that don't carry this scope. offline.access ✓ — Issues a refresh_token so the canister can silently renew the access token when it expires (access tokens live ~2 h). Omit this and users re-authorise every two hours. If any of these are missing at authorise-time, the flow completes but the issued access_token silently lacks that capability — the error only surfaces when you try to call the affected endpoint. Storing tokens The bearer never leaves the canister . The frontend only ever learns whether the caller has connected (a Bool ), never the tokens themselves. Same rules as OpenAI's per-user bearer: A Map<Principal, XAuth> keyed by caller. Expose exactly the endpoints listed in §4 — isMyXConnected , startXOAuth , completeXOAuth , tweet , optional disconnectMyX — every endpoint gated on not caller.isAnonymous() . Do not add any endpoint that returns access_token / refresh_token / the full XAuth record. Internal reads ( Map.get(xAuthByUser, ..., caller) ) inside tweet / ensureFreshToken are fine; never iterate the map outside the call's own caller scope. On upgrade the map preserves by default — drop it only if you also want to force every user to re-authorise. 3. is_replicated = ?false is REQUIRED Same priority order as extension-openai 's §3: Security. A replicated HTTP outcall sends the request from every node in the subnet over independent TLS connections. Each connection carries Authorization: Bearer <access_token> . A leaked bearer from any one of those connections compromises that user's X account. Billing. Replicated outcalls produce N parallel API calls. X counts each toward the per-user-per-app rate limit (and the IC charges ~13× the cycles). One subnet-wide tweet call quickly trips X's rate limit. Determinism. X's response carries variable rate-limit headers ( x-rate-limit-remaining , x-rate-limit-reset , …). Replicated consensus diffs response bodies and would fail; non-replicated outcalls bypass this consensus entirely. → Always: is_replicated = ?false on the Config . 4. Canonical layout This is the default shape: admin Client ID + per-user OAuth . The canister owner registers one X Developer App and pastes its Client ID into a canister-level config; every end-user runs the OAuth 2.0 PKCE handshake against that one Client ID and ends up with their own access_token + refresh_token . The example spans five files: src/backend/main.mo — the actor: state + include s only. src/backend/migrations/00000000_000000.mo — the migration chain head. src/backend/mixins/x-config.mo — admin Client ID ( isXClientIdConfigured , setXClientId ). src/backend/mixins/x-posting.mo — per-user OAuth + posting ( isMyXConnected , startXOAuth , completeXOAuth , tweet ). src/backend/lib/x.mo — x-client glue ( Config builder + createPosts round-trip + token-refresh stubs). import Map "mo:core/Map"; import Principal "mo:core/Principal"; import AccessControl "mo:caffeineai-authorization/access-control"; import MixinAuthorization "mo:caffeineai-authorization/MixinAuthorization"; import MixinXConfig "mixins/x-config"; import MixinXPosting "mixins/x-posting"; import LibX "lib/x"; actor { // Authorization plumbing from extension-authorization. Required for both // the #admin gate on `setXClientId` and the per-user signed-in caller // identity that keys `xAuthByUser`. let accessControlState : AccessControl.AccessControlState; include MixinAuthorization(accessControlState, null); // Admin-set X Developer App Client ID. Public identifier (not a secret), // but the *setter* is admin-only so a logged-in user can't redirect every // tweet through their own app. let xClientId : { var value : ?Text }; include MixinXConfig(accessControlState, xClientId); // Per-user OAuth tokens. Never iterated except by the calling principal. let xAuthByUser : Map.Map<Principal, LibX.XAuth>; include MixinXPosting(xClientId, xAuthByUser); }; The migration chain head: import Map "mo:core/Map"; import AccessControl "mo:caffeineai-authorization/access-control"; module { type XAuth = { access_token : Text; refresh_token : Text; expires_at : Nat64; scope : [Text]; }; type NewActor = { accessControlState : AccessControl.AccessControlState; xClientId : { var value : ?Text }; xAuthByUser : Map.Map<Principal, XAuth>; }; public func migration(_old : {}) : NewActor { { accessControlState = AccessControl.initState(); xClientId = { var value : ?Text = null }; xAuthByUser = Map.empty<Principal, XAuth>(); }; }; }; import AccessControl "mo:caffeineai-authorization/access-control"; import Runtime "mo:core/Runtime"; // Admin-gated X Developer App Client ID. Mounted by `main.mo` via `include`. // Pairs with `MixinAuthorization` to power the role check. mixin ( accessControlState : AccessControl.AccessControlState, xClientId : { var value : ?Text }, ) { public query func isXClientIdConfigured() : async Bool { xClientId.value != null;
このスキルを起動するキーワード。クリックでコピーできます。

このスキルにはトリガーワードがありません。

ダウンロードした .skill に含まれるフィールド。
フィールド 説明
formatフォーマット識別子(skill/v1)
skill_idスキル固有 ID
nameスキル名
versionバージョン
description説明
categoryカテゴリ(配列)
trigger_wordsトリガーワード
tagsタグ
sourceソース
source_urlソース URL(本ページ)
exported_atエクスポート日時(ダウンロード毎)
system_promptシステムプロンプト本文
model_configモデル設定:provider / model / temperature / max_tokens / top_p
examplesサンプル
install_guide各プラットフォームの導入説明(Coze / Dify / Claude / カスタム)
同じスキルを各プラットフォーム形式で出力できます。
.skill 標準形式。system_prompt と model_config を収録し、任意の Agent で利用可能 ダウンロード
.skillpro 拡張形式。scripts / tools / dependencies / hooks を含む ダウンロード
.json 純粋な JSON 出力。system_prompt とモデル設定のみ ダウンロード
Coze frontmatter 付き Markdown。Coze へのインポート用 ダウンロード
Dify Dify DSL。アプリ作成後にそのままインポート ダウンロード

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。