Skip to content

Add VK (VKontakte) as a platform - #311

Open
gynsus wants to merge 7 commits into
trypostit:mainfrom
gynsus:pr/vk-platform
Open

Add VK (VKontakte) as a platform#311
gynsus wants to merge 7 commits into
trypostit:mainfrom
gynsus:pr/vk-platform

Conversation

@gynsus

@gynsus gynsus commented Aug 30, 2026

Copy link
Copy Markdown

Why

VK (VKontakte) is the dominant social network for Russian-speaking audiences, and self-hosted TryPost users publishing there currently have to do it by hand. This PR adds VK as a full platform: connect, publish (text, photos, video), and analytics.

The connect flow is deliberately token-based, not OAuth. VK stopped granting the wall scope to newly registered OAuth apps years ago, so a stock "log in with VK" integration cannot post to walls at all. What still works, verified against the live API:

  • a community access token (community → Manage → API usage → Access tokens) — wall.post with owner_id=-<group> + from_group=1 is allowed for these regardless of the app type that issued them; this is the recommended path and the one the form suggests first;
  • a user access token issued by a standalone app the user owns (wall, photos, groups, video, offline scopes) — needed for profile walls and for video upload, which VK only allows with user tokens.

What changed

  • Platform::Vk + ContentType::VkPost (16,000-char limit, VK's actual cap), lang strings for all 16 locales, platform tile/logo/theme.
  • Connect (VkController): one token field for both token kinds. A community token is detected by a successful-but-empty users.get (the method accepts community tokens and just returns an empty list without user_ids — it does not error). Three live-API quirks shaped the flow:
    • VK has no API to tell which community a token belongss to, so for community tokens the form asks for the community address (vk.com/club…, screen name, or bare id — all normalized) and resolves it via groups.getById.
    • Ownership is then proven with groups.getCallbackConfirmationCode, which only answers for the token's own community. (groups.getOnlineStatus looked right for this but fails with "group messages is disabled" for communities that turned messages off.)
    • For user tokens, users.get + groups.get(filter=admin,editor) builds a picker of publishable walls (own profile + administered communities).
  • Publish (VkPublisher): wall.post with attachments; photos via the three-step getWallUploadServer → upload → saveWallPhoto flow (images optimized first), video via video.save with wallpost=0 so the video attaches to our post instead of auto-publishing. A failed single attachment is skipped with a log, not a sunk post. VK reports errors as HTTP 200 + error object; VkPublishException maps codes to the shared error categories (rate limit / permission / media format).
  • Analytics (VkAnalytics): community member count on the account level; views/likes/reposts/comments per post via wall.getById. Handles both the v5.199 response shape (response.groups[] / response.items[]) and the older flat one.
  • Verification (ConnectionVerifier): users.get for user tokens, groups.getById for community tokens (which users.get can't verify); VK error 5 is the confirmed-dead-token signal.

Testing

  • New suites: VkControllerTest (9 tests: both token kinds, community mismatch/unknown address, VK-side errors), VkPublisherTest, VkAnalyticsTest, VkPublishExceptionTest, plus ConnectionVerifierTest additions for both verify paths.
  • Battle-tested on a self-hosted production instance: both token kinds connected, wall posts with photos published to two communities, analytics rendering member counts and per-post metrics.

What only you can decide

Token-based connect is unusual for this codebase — every other platform is OAuth. We kept the UI to the same two-step popup pattern as LinkedIn's identity picker, but if you'd rather gate VK behind a SELF_HOSTED-style env flag until it settles, that's a small change on top.

gynsus added 7 commits August 30, 2026 16:07
VK publishes to a community or profile wall via wall.post with a user
access token (scope: wall, photos, groups, video, offline) — VK no longer
grants the wall scope to new OAuth apps, so the connect flow accepts a
token directly, modeled on the Bluesky credential form: validate the
token, list manageable walls (own profile + administered communities),
store the chosen wall as the account.

Publisher uploads photos via getWallUploadServer/saveWallPhoto and videos
via video.save, then attaches them to wall.post. VK reports failures as
HTTP 200 with an error object; error 5 maps to TokenExpiredException,
rate-limit and permission codes to their categories.
VK joins the analytics page: account level shows the community member
count (profile wall: follower count) via groups.getById/users.get; post
level pulls views, likes, reposts and comments from wall.getById. Deeper
community stats need the stats scope the connect flow doesn't request, so
they are deliberately out.
wall.post with a user token is limited to standalone apps; community
access tokens publish to their community regardless of the issuing app
type. users.get error 27 tells the two apart on the shared token field;
verification for community-token accounts goes through groups.getById.
VK has no API to resolve which community a token belongs to
(groups.getById without group_ids returns nothing for community
tokens), so the form asks for the community link/screen name and
groups.getOnlineStatus — callable only with the community's own
token — proves the key matches it.
…or 27

users.get is callable with a community access token — without user_ids
it returns an empty response instead of a group-auth error, so a
successful-but-empty users.get is the community-token signal.
groups.getOnlineStatus fails with 'group messages is disabled' for
communities that turned messages off; getCallbackConfirmationCode works
with the community's own token regardless and still rejects foreign
tokens.
@paulocastellano

Copy link
Copy Markdown
Contributor

@gynsus I'm looking into this; do I need a Russian VPN to install this app?

@gynsus

gynsus commented Sep 1, 2026

Copy link
Copy Markdown
Author

Should work worldwide, no VPN needed — vk.com and api.vk.com are not geo-restricted. For the easiest test path you don't even need to register a VK app: any VK account (a non-Russian phone number should work for signup) → create a community → Manage → API usage → Access tokens → create a token with photos/wall/manage → paste it into TryPost's Connect VK form. That exercises the recommended community-token flow end to end. The user-token path needs a standalone app of your own, but it's optional — happy to walk you through it if you want to test video upload too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants