Commit graph

726 commits

Author SHA1 Message Date
Paulo Castellano
4bb7d450a3 chore(release): link GitHub release in changelog email + add v1.0.2 artifacts
Tweak the /release command's email template (GitHub release link in the byline,
"Paulo from TryPost.it" signature) and add the generated v1.0.2 release notes.
2026-06-16 11:09:25 -03:00
Paulo Castellano
3acaafb81e test(automations): extract inline feed XML into shared fixtures
Move the RSS sample feeds out of inline heredocs/constants in FetchRssNodeTest
into tests/fixtures/feeds/, matching the parser fixtures. Centralize the
feedFixture() loader in tests/Pest.php and use it across the feed tests.
2026-06-16 11:06:46 -03:00
Paulo Castellano
246a159f34 feat(automations): multi-format RSS/Atom feeds with dynamic variables
Replace the RSS-2.0-only SimpleXML parser with SimplePie so the Fetch RSS
node reads Atom 1.0 (YouTube, GitHub, The Verge…) and RSS 2.0 + namespace
extensions (dc:, content:, media:, yt:, itunes:). Each item exposes stable
cross-format aliases (title, link, date, content, author, …) plus every
namespaced field flattened for use as {{ fetched.* }}.

Add a feed-inspection endpoint that discovers a feed's real fields and feeds
them into the editor's expression autocomplete. Allow {{ }} expressions in the
feed URL via a ResolvableUrl rule. Parsing moves to a dedicated FeedParser
service; the fetch keeps the SSRF guard and gains XXE-safe parsing.
2026-06-16 10:56:08 -03:00
Paulo Castellano
b18b9b2303
Merge pull request #98 from trypostit/feat/discord-widget
feat(discord): global floating Discord widget
2026-06-15 13:28:20 -03:00
Paulo Castellano
f41b28c31d feat(discord): global floating Discord widget with PostHog tracking
- Fixed bottom-right Discord button (indies-club style) linking to
  https://trypost.it/discord, mounted globally in AppSidebarLayout
- Captures discord.clicked (placement: floating_widget) via captureEvent
- i18n label + aria-label in common.php (en, es, pt-BR)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 13:23:03 -03:00
Paulo Castellano
c71234337d
Merge pull request #96 from trypostit/fix/past-due-no-forced-subscribe
Past-due access, reliable checkout.completed, and complete morph map
2026-06-14 16:18:51 -03:00
Paulo Castellano
65116de859 Register all models in the morph map + lock it with a test
Add the five automation models to Relation::enforceMorphMap(), document the
all-models-must-be-mapped rule in CLAUDE.md, and add MorphMapTest to fail
when any app/Models class is missing from the map.
2026-06-14 16:12:49 -03:00
Paulo Castellano
f223cf14ce Fire checkout.completed reliably for trial-with-card checkouts
A trial-with-card subscription is already subscribed() (status trialing)
by the time the webhook lands, so /billing/processing usually mounts
already-active and the false->true poll transition the event depended on
never happened — only 3 of 66 real subscriptions emitted checkout.completed.

Complete the purchase from whichever path runs first (onMounted when already
active, or the poll transition), de-duplicated per checkout session via a
one-time Cache::add gate on session_id so back-button/refresh can't re-fire.
2026-06-14 16:12:49 -03:00
Paulo Castellano
8e334cd676 Keep past_due subscribers in-app instead of forcing re-subscribe
A past_due subscription made subscribed() return false (Cashier default),
so EnsureAccountReady redirected to /subscribe — which starts a brand-new
checkout and creates a second subscription. Past-due users already have a
subscription; they only need to update their payment method.

Enable Cashier::keepPastDueSubscriptionsActive() so past_due counts as
active and users keep navigating. Surface a past-due notice in the sidebar
footer linking to the Stripe billing portal (not /subscribe). Both trial
modes (subscription trial / generic trial) are unaffected.
2026-06-14 15:46:17 -03:00
Paulo Castellano
11c27dfd62
Merge pull request #95 from trypostit/feat/telegram-channel
Add Telegram publishing channel
2026-06-14 14:46:33 -03:00
Paulo Castellano
c9f086ea94 Use formatNumberCompact helper in post platform metrics 2026-06-14 14:09:39 -03:00
Paulo Castellano
389177c25b Soften Telegram preview bubble shadow (drop trypost ink shadow) 2026-06-14 14:07:36 -03:00
Paulo Castellano
0c3c837bd3 Polish Telegram preview: simulate channel reactions 2026-06-14 13:58:14 -03:00
Paulo Castellano
548138f277 Render Telegram subscribers as an icon pill with divider before reactions 2026-06-14 13:53:57 -03:00
Paulo Castellano
a5b33f5b0a Show Telegram subscribers on post metrics; render reactions as compact pills 2026-06-14 13:50:12 -03:00
Paulo Castellano
fc1d51ca60 Telegram connect dialog: drop footer Close button, top-align icon 2026-06-14 13:35:03 -03:00
Paulo Castellano
b9fa8be513 Group Telegram service classes under Services/Social/Telegram 2026-06-14 13:32:03 -03:00
Paulo Castellano
80b68088a0 Type Telegram media kinds with a TelegramMediaType enum 2026-06-14 13:23:05 -03:00
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