Commit graph

708 commits

Author SHA1 Message Date
Paulo Castellano
b6a3cb886c Centralize Telegram Bot API URL/token construction in a TelegramApi helper 2026-06-14 12:41:19 -03:00
Paulo Castellano
e907ad087f Lock Telegram @mention passthrough in sanitizer test 2026-06-14 11:40:55 -03:00
Paulo Castellano
c719480cf1 Harden Telegram reactions/analytics from PR review; add scoping + edge-case tests 2026-06-14 11:37:04 -03:00
Paulo Castellano
ba246a3ddd Add Telegram analytics: per-post reactions (webhook) + channel subscribers 2026-06-14 11:18:40 -03:00
Paulo Castellano
ce5d59a149 Detect Telegram connection via broadcast instead of polling 2026-06-14 10:58:13 -03:00
Paulo Castellano
f5d6c4a186 Fetch Telegram channel photo synchronously in the connect action (drop avatar job) 2026-06-14 10:30:17 -03:00
Paulo Castellano
f194bde58d Fall back to display_name for disconnect confirmation when username is null (Telegram) 2026-06-14 10:21:52 -03:00
Paulo Castellano
b9cc12aef7 Fetch Telegram channel photo as avatar; extract ConnectTelegramChannel action 2026-06-14 10:18:44 -03:00
Paulo Castellano
58eab0619b Use TelegramConnectStatus enum for connect status instead of magic strings 2026-06-14 09:59:36 -03:00
Paulo Castellano
92e6425906 Use Platform enum instead of magic string in AddSocialDialog 2026-06-14 09:55:50 -03:00
Paulo Castellano
ec5a3a3b16 Add cursor-pointer and tooltip to Telegram copy button 2026-06-14 09:42:31 -03:00
Paulo Castellano
d5da44fd75 Fix Telegram dialog overflow (min-w-0 grid item), round icon, dedupe copy toast 2026-06-14 09:36:42 -03:00
Paulo Castellano
33bc001977 Truncate the long Telegram connect code so the dialog doesn't overflow 2026-06-14 09:32:09 -03:00
Paulo Castellano
e72bf51c38 Make webhook host configurable via WEBHOOK_URL (sendkit-style domain group) 2026-06-14 09:31:00 -03:00
Paulo Castellano
cc61adbac2 Move webhook routes into dedicated routes/webhook.php 2026-06-14 09:23:46 -03:00
Paulo Castellano
d729c34c0e Harden Telegram connect: one-time code, drop session, decode validation; switch icon to png; widen add-social dialog 2026-06-14 09:18:24 -03:00
Paulo Castellano
2284596f9d Make Telegram connect codes stateless: drop the model/table, use a signed code + session 2026-06-14 08:57:38 -03:00
Paulo Castellano
816d367dbe Add return type to telegram exception test helper 2026-06-13 22:58:04 -03:00
Paulo Castellano
c096092c7b Polish PR: status FormRequest, sanitizer anchor guard, webhook limit, dedupe OAuth popup 2026-06-13 22:48:21 -03:00
Paulo Castellano
2585d89cb4 Address PR review: connect-status enum, publisher cleanup, fill test gaps 2026-06-13 22:38:23 -03:00
Paulo Castellano
deb1c6fa69 Extract Telegram webhook registration into an action 2026-06-13 22:20:52 -03:00
Paulo Castellano
f378469842 Wire Telegram into the post composer (preview, content type, logo) 2026-06-13 22:10:12 -03:00
Paulo Castellano
b504243212 Document Telegram env vars in docker .env example 2026-06-13 22:02:02 -03:00
Paulo Castellano
b6605ea228 Document Telegram env vars in .env.example 2026-06-13 22:01:37 -03:00
Paulo Castellano
79acdccc45 Add Telegram connection UI (connect dialog + code polling) 2026-06-13 21:58:49 -03:00
Paulo Castellano
a4cf8aa4ce Add Telegram connection flow (controller + webhook)
Connect a channel by issuing a one-time code the user posts as /connect <code>
in their channel. A secret-token-guarded webhook matches the code, links the
channel as a SocialAccount (chat_id in meta), and records it on the request so
the connect endpoint can poll for completion. Adds the TelegramConnectRequest
model + migration, the connect/status endpoints, the public webhook route (CSRF
exempt), a ConnectionVerifier branch (getChat liveness), and a telegram:set-webhook
command. Tests cover the code issue, webhook link, secret rejection, expired/
unknown codes, status polling, and the command.
2026-06-13 21:39:03 -03:00
Paulo Castellano
9634e88e5d Add Telegram publishing (backend foundation)
Register Telegram as a platform: Platform/ContentType enum cases, a
platforms.telegram config block (shared bot token via env), TelegramPublisher
(sendMessage / sendPhoto|Video|Document / sendMediaGroup over the Bot API, HTML
parse mode, 4096 limit with long text split off a 1024 caption), wired into the
publisher dispatch. Add a Telegram ContentSanitizer branch (Telegram-allowed
HTML + ampersand escaping), MediaOptimizer/profile-url/factory support, and the
TelegramPublishException. Tests cover text, single media, album, long-text split,
overflow, API errors, private-channel URLs, and sanitization.
2026-06-13 21:25:31 -03:00
Paulo Castellano
986e2661f9
Merge pull request #94 from trypostit/fix/bluesky-publisher-hardening
Harden BlueskyPublisher: link facets, conventions, NSID lexicon, tests
2026-06-13 20:38:09 -03:00
Paulo Castellano
9864ea96b6 Cover handle-resolution errors and configured web-app host
Add a test that a network error during handle resolution degrades the mention
to plain text instead of failing the post (exercises the resolveHandleToDid
try/catch), and a test asserting the post URL is built from the configured
web_app host (a revert to a hardcoded host would now fail).
2026-06-13 20:30:41 -03:00
Paulo Castellano
444d522012 Lock lexicon values and the URL paren-keep branch in tests
Add a unit test asserting each BlueskyLexicon constant equals its AT Protocol
NSID — a wrong value (not just a typo'd name) would otherwise fail silently at
runtime. Add a test that a URL with a matching '(' keeps its trailing ')'
(Wikipedia-style), covering the previously-untested keep branch of the trim.
2026-06-13 20:25:05 -03:00
Paulo Castellano
8cfdd4c128 Centralize AT Protocol NSIDs in BlueskyLexicon
The Bluesky lexicon identifiers (createRecord, createSession, feed.post, facet
types, etc.) were repeated as magic strings across BlueskyPublisher,
BlueskyAnalytics, ConnectionVerifier and BlueskyController, where a typo fails
silently at runtime as "Invalid request". Define them once as named constants
so a typo is an undefined-constant error instead. Tests keep the literal NSIDs
as the independent contract.
2026-06-13 20:18:39 -03:00
Paulo Castellano
c86596ada3 Assert image embed and 4-image cap in tests
The image tests only checked that createRecord was sent — the faked empty
download meant no blob was ever attached, so they never verified the embed.
Drive a real (mocked) optimize + upload and assert the createRecord record
carries an app.bsky.embed.images embed, and that six images upload exactly four.
2026-06-13 20:07:25 -03:00
Paulo Castellano
84052a2f1c Tidy BlueskyPublisher conventions
Import Exception instead of referencing it inline, read the blob via data_get,
source the web-app host from config (new bluesky.web_app key) instead of
hardcoding bsky.app, and drop the getUtf8ByteOffset no-op — the PREG /u offset
is already in bytes, so the call sites use it directly.
2026-06-13 20:04:40 -03:00
Paulo Castellano
2ff34369e0 Stop URL facets swallowing trailing punctuation
The URL regex captured trailing sentence punctuation and an unmatched closing
paren ("see https://x.com).") into the link facet, so Bluesky rendered the
punctuation as part of a malformed link. Trim it like the official atproto
tokenizer. Add tests for the trim and for byte-offset correctness after
multibyte characters (emoji/accents).
2026-06-13 20:02:54 -03:00
Paulo Castellano
7baf1b7df5
Merge pull request #93 from Schrall/fix/bluesky-mention-facet-resolve-did
Fix: resolve Bluesky @mention handles to DIDs (mentions caused "Invalid post data")
2026-06-13 19:57:17 -03:00
Paulo Castellano
60094c9316 Simplify handle resolution to a single AppView call
resolveHandle is a public read served by the AppView, so drop the PDS-first
auth, the multi-endpoint fallback loop and the threaded service/account args in
favor of one unauthenticated GET against config public_appview — matching how
BlueskyAnalytics already reads from the AppView. The try/catch stays so a
network error degrades the mention to plain text instead of failing the post.
Remove the now-obsolete fallback test.
2026-06-13 18:41:43 -03:00
Paulo Castellano
5227cde5c7 Cache unresolvable handles and tighten offset types
Memoize by key (array_key_exists) so a handle that fails to resolve is tried
once per post instead of on every repeat occurrence, and add a test that a
repeated handle resolves once. Cast the PREG_OFFSET_CAPTURE offsets to int to
match getUtf8ByteOffset's signature, and reword the resolver docblock to say
"configured default service" instead of naming bsky.social.
2026-06-13 18:35:30 -03:00
Paulo Castellano
9f0c264c39 Cover mention endpoint fallback and partial resolution
Add tests for the two untested branches of the DID resolution: falling back to
the public AppView when the account's PDS can't resolve a handle, and a post
mixing a resolvable mention (kept as a facet) with an unresolvable one (a 200
without a DID, left as plain text). The prior tests only hit the happy path and
the all-endpoints-fail path via a wildcard that masked the fallback.
2026-06-13 18:21:16 -03:00
Paulo Castellano
409c6f0139 Drop the resolution warning log and fake createRecord via config
Remove the final Log::warning (and the lastError/lastEndpoint tracking that only
fed it) — the per-endpoint debug log already covers diagnosis. Fake the
createRecord endpoint from config('trypost.platforms.bluesky.default_service')
in the new tests instead of hardcoding the host.
2026-06-13 18:19:04 -03:00
Paulo Castellano
5d80caffef Shorten the mention-facet comment to one line 2026-06-13 18:16:24 -03:00
Paulo Castellano
5cafac34c1 Resolve Bluesky endpoints from config and import Throwable
Use the existing config('trypost.platforms.bluesky.public_appview') and
default_service keys for the handle-resolution fallbacks instead of hardcoding
the hosts, so a self-hosted override (BLUESKY_PUBLIC_APPVIEW) is honored here
like it already is in BlueskyAnalytics. Import Throwable instead of referencing
it inline.
2026-06-13 18:15:31 -03:00
Paulo Castellano
2641cc66fe
Merge branch 'main' into fix/bluesky-mention-facet-resolve-did 2026-06-13 18:09:00 -03:00
Paulo Castellano
44fb42b500
Merge pull request #88 from trypostit/feat/automations-module
Automations ⚙️
2026-06-13 18:08:04 -03:00
Paulo Castellano
757a215d0d Back run/node status and node type with TS consts
Add NodeRunStatus and RunStatus consts mirroring the backend enums, and route
the test panel and invocations list through them (plus the existing NodeType)
instead of bare 'completed'/'failed'/'fetch_rss' string checks. Type the run/
node-run interfaces with the value unions, extract the status tile classes into
a helper, and render the node type via i18n like the invocations list already does.
2026-06-13 17:50:23 -03:00
Paulo Castellano
19f331741c Use Inertia's useHttp for automation JSON calls
The test-run and invocation-detail requests used raw fetch with a hand-rolled
CSRF header. Switch to Inertia v3's useHttp (the standalone XHR client already
used across the app), which handles CSRF, headers and error payloads — dropping
the manual plumbing and reading the 422 message from error.response.data.
2026-06-13 17:41:48 -03:00
Paulo Castellano
cf3cbfba9e Simplify the cron-to-schedule inference
Rewrite inferScheduleFromCron: bail once on a pinned month, group the branches
by cadence (sub-hourly / hourly / daily / weekly / monthly), and share the
hour+minute via one `clock` object instead of repeating the same guards in
eight stacked conditionals. Behavior-preserving — verified the generate↔infer
round-trip across every preset.
2026-06-13 17:31:02 -03:00
Paulo Castellano
c79b4a4a84 Type node enum props with their value unions
The required method/unit/operator/mode props were typed as plain string after
dropping their defaults. Use the matching *Value union types (HttpMethodValue,
DelayUnitValue, ConditionOperatorValue, PublishModeValue) so the renderers are
constrained to real enum values, matching the config panels.
2026-06-13 17:15:20 -03:00
Paulo Castellano
1ac4795575 Drop redundant method default in webhook/http nodes
Webhook and HTTP nodes are always created with a method, so the ?? default was
dead. Make the prop required and remove the now-unused HttpMethod import,
matching the unit/operator/mode cleanup.
2026-06-13 17:13:32 -03:00
Paulo Castellano
9b3e9e8307 Translate node summary labels in the canvas
The Generate/Delay/Condition/Publish node summaries rendered English prose and
raw enum values (account/accounts, single, hours, contains, scheduled · +N min).
Route them through i18n: pluralize the account count, and reuse the existing
unit/operator/mode/format labels in all three locales. Drop the redundant
?? defaults where the node is always created with the value (unit, operator,
mode) and tighten those prop types to required.
2026-06-13 17:10:38 -03:00
Paulo Castellano
90e9e4bee6 Centralize timezone display in the date helper
Add date.getTimezoneAbbr() and route every "GMT-x" abbreviation through it,
removing the three inline dayjs().format('z') copies (schedule-summary,
TriggerNodeConfig, PickTimePopover) and the duplicate userTimezone helper.
Drop the pointless computed wrapper for a value that never changes, and rename
snap5 to snapToFiveMinutes.
2026-06-13 16:46:34 -03:00