-
Notifications
You must be signed in to change notification settings - Fork 7
Upgrade staging #212
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
Upgrade staging #212
Changes from all commits
08f36a3
713ec36
06c695d
5e4589b
c46b92d
316a546
dc44cc7
00407cc
fbfc117
535de16
4211446
9fc8273
3ee14d9
e639b07
c33670f
7e5300b
274b1fe
1ec8989
c36fa5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,261 @@ | ||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Distributor URL utility functions | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * This module provides functions to generate appropriate URLs for different | ||||||||||||||||||||||
| * distributor types based on their configuration. | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export interface DistributorConfig { | ||||||||||||||||||||||
| plugin: string; | ||||||||||||||||||||||
| config?: Record<string, unknown>; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Checks if a value is a template variable (contains {{ }}) | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function isTemplateVariable(value: unknown): boolean { | ||||||||||||||||||||||
| return typeof value === "string" && value.includes("{{"); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Safely gets a string value from config, returning null if it's a template | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getConfigString( | ||||||||||||||||||||||
| config: Record<string, unknown> | undefined, | ||||||||||||||||||||||
| key: string, | ||||||||||||||||||||||
| ): string | null { | ||||||||||||||||||||||
| const value = config?.[key]; | ||||||||||||||||||||||
| if (typeof value === "string" && !isTemplateVariable(value)) { | ||||||||||||||||||||||
| return value; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Generate URLs for RSS distributor | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getRssUrl(config: Record<string, unknown> | undefined): string | null { | ||||||||||||||||||||||
| // RSS distributors have a serviceUrl in their config that points to the RSS feed | ||||||||||||||||||||||
| const serviceUrl = getConfigString(config, "serviceUrl"); | ||||||||||||||||||||||
| return serviceUrl; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Generate URLs for Twitter distributor | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getTwitterUrl(config: Record<string, unknown> | undefined): string { | ||||||||||||||||||||||
| // Twitter links to the specific profile if available | ||||||||||||||||||||||
| const username = | ||||||||||||||||||||||
| getConfigString(config, "username") || getConfigString(config, "handle"); | ||||||||||||||||||||||
| if (username) { | ||||||||||||||||||||||
| return `https://twitter.com/${username.replace("@", "")}`; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return "https://twitter.com"; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Generate URLs for Telegram distributor | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getTelegramUrl(config: Record<string, unknown> | undefined): string { | ||||||||||||||||||||||
| // Telegram links to the channel if chatId is available | ||||||||||||||||||||||
| const chatId = | ||||||||||||||||||||||
| getConfigString(config, "chatId") || getConfigString(config, "channelId"); | ||||||||||||||||||||||
| if (chatId) { | ||||||||||||||||||||||
| const cleanChatId = chatId.replace("@", ""); | ||||||||||||||||||||||
| return `https://t.me/${cleanChatId}`; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return "https://telegram.org"; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Generate URLs for Notion distributor | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getNotionUrl(config: Record<string, unknown> | undefined): string { | ||||||||||||||||||||||
| // Notion links to the table/database if available | ||||||||||||||||||||||
| const databaseId = getConfigString(config, "databaseId"); | ||||||||||||||||||||||
| if (databaseId) { | ||||||||||||||||||||||
| return `https://notion.so/${databaseId}`; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return "https://notion.so"; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Generate URLs for Crosspost distributor | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getCrosspostUrl(config: Record<string, unknown> | undefined): string { | ||||||||||||||||||||||
| // Crosspost links to the Open Crosspost platform | ||||||||||||||||||||||
| // Could potentially link to specific account if signerId is available | ||||||||||||||||||||||
| const signerId = getConfigString(config, "signerId"); | ||||||||||||||||||||||
| if (signerId) { | ||||||||||||||||||||||
| return `https://near.social/mob.near/widget/MainPage.N.Profile.Page?accountId=${signerId}`; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return "https://opencrosspost.com"; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Generate URLs for Discord distributor | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getDiscordUrl(config: Record<string, unknown> | undefined): string { | ||||||||||||||||||||||
| // Discord links to the specific channel if available | ||||||||||||||||||||||
| const channelId = getConfigString(config, "channelId"); | ||||||||||||||||||||||
| if (channelId) { | ||||||||||||||||||||||
| // Discord channel URLs format: https://discord.com/channels/serverId/channelId | ||||||||||||||||||||||
| // Since we don't have serverId, we use @me which works for direct channel links | ||||||||||||||||||||||
| return `https://discord.com/channels/@me/${channelId}`; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return "https://discord.com"; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Generate URLs for NEAR Social distributor | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getNearSocialUrl(config: Record<string, unknown> | undefined): string { | ||||||||||||||||||||||
| // NEAR Social links to the specific account if available | ||||||||||||||||||||||
| const accountId = getConfigString(config, "accountId"); | ||||||||||||||||||||||
| if (accountId) { | ||||||||||||||||||||||
| return `https://near.social/mob.near/widget/MainPage.N.Profile.Page?accountId=${accountId}`; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return "https://near.social"; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Generate URLs for Supabase distributor | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getSupabaseUrl(config: Record<string, unknown> | undefined): string { | ||||||||||||||||||||||
| // Supabase links to the project dashboard if URL is available | ||||||||||||||||||||||
| const url = getConfigString(config, "url"); | ||||||||||||||||||||||
| if (url) { | ||||||||||||||||||||||
| // Extract project reference from Supabase URL | ||||||||||||||||||||||
| const match = url.match(/https:\/\/([^.]+)\.supabase\.co/); | ||||||||||||||||||||||
| if (match) { | ||||||||||||||||||||||
| const projectRef = match[1]; | ||||||||||||||||||||||
| return `https://supabase.com/dashboard/project/${projectRef}`; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
Comment on lines
+129
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Strengthen URL validation for Supabase project extraction. The regex pattern for extracting Supabase project references could be more robust and should handle edge cases. - const match = url.match(/https:\/\/([^.]+)\.supabase\.co/);
+ const match = url.match(/^https:\/\/([a-zA-Z0-9-]+)\.supabase\.co(?:\/|$)/);This ensures:
📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return "https://supabase.com"; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Get the clean plugin name without the @curatedotfun/ prefix | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| function getPluginName(plugin: string): string { | ||||||||||||||||||||||
| return plugin.replace("@curatedotfun/", ""); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Main function to get distributor URL based on plugin type and configuration | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| export function getDistributorUrl( | ||||||||||||||||||||||
| distributor: DistributorConfig, | ||||||||||||||||||||||
| ): string | null { | ||||||||||||||||||||||
| const pluginName = getPluginName(distributor.plugin); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| switch (pluginName) { | ||||||||||||||||||||||
| case "rss": | ||||||||||||||||||||||
| return getRssUrl(distributor.config); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "twitter": | ||||||||||||||||||||||
| case "twitter-distributor": | ||||||||||||||||||||||
| return getTwitterUrl(distributor.config); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "telegram": | ||||||||||||||||||||||
| case "telegram-distributor": | ||||||||||||||||||||||
| return getTelegramUrl(distributor.config); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "notion": | ||||||||||||||||||||||
| case "notion-distributor": | ||||||||||||||||||||||
| return getNotionUrl(distributor.config); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "crosspost": | ||||||||||||||||||||||
| case "crosspost-distributor": | ||||||||||||||||||||||
| return getCrosspostUrl(distributor.config); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "discord": | ||||||||||||||||||||||
| case "discord-distributor": | ||||||||||||||||||||||
| return getDiscordUrl(distributor.config); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "near-social": | ||||||||||||||||||||||
| case "near-social-distributor": | ||||||||||||||||||||||
| case "nearsocial": | ||||||||||||||||||||||
| return getNearSocialUrl(distributor.config); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "supabase": | ||||||||||||||||||||||
| case "supabase-distributor": | ||||||||||||||||||||||
| return getSupabaseUrl(distributor.config); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| default: | ||||||||||||||||||||||
| // For unknown distributors, return null to show badge without link | ||||||||||||||||||||||
| return null; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Get a display-friendly name for the distributor | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| export function getDistributorDisplayName( | ||||||||||||||||||||||
| distributor: DistributorConfig, | ||||||||||||||||||||||
| ): string { | ||||||||||||||||||||||
| const pluginName = getPluginName(distributor.plugin); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Convert plugin names to display names | ||||||||||||||||||||||
| switch (pluginName) { | ||||||||||||||||||||||
| case "near-social": | ||||||||||||||||||||||
| case "nearsocial": | ||||||||||||||||||||||
| return "NEAR Social"; | ||||||||||||||||||||||
| case "rss": | ||||||||||||||||||||||
| return "RSS"; | ||||||||||||||||||||||
| default: | ||||||||||||||||||||||
| // Capitalize first letter for other distributors | ||||||||||||||||||||||
| return pluginName.charAt(0).toUpperCase() + pluginName.slice(1); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * Check if a distributor has a functional URL (not just a fallback) | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| export function hasSpecificUrl(distributor: DistributorConfig): boolean { | ||||||||||||||||||||||
| const pluginName = getPluginName(distributor.plugin); | ||||||||||||||||||||||
| const config = distributor.config; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| switch (pluginName) { | ||||||||||||||||||||||
| case "rss": | ||||||||||||||||||||||
| return !!getConfigString(config, "serviceUrl"); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "twitter": | ||||||||||||||||||||||
| case "twitter-distributor": | ||||||||||||||||||||||
| return !!( | ||||||||||||||||||||||
| getConfigString(config, "username") || getConfigString(config, "handle") | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "telegram": | ||||||||||||||||||||||
| case "telegram-distributor": | ||||||||||||||||||||||
| return !!( | ||||||||||||||||||||||
| getConfigString(config, "chatId") || | ||||||||||||||||||||||
| getConfigString(config, "channelId") | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "notion": | ||||||||||||||||||||||
| case "notion-distributor": | ||||||||||||||||||||||
| return !!getConfigString(config, "databaseId"); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "crosspost": | ||||||||||||||||||||||
| case "crosspost-distributor": | ||||||||||||||||||||||
| return !!getConfigString(config, "signerId"); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "discord": | ||||||||||||||||||||||
| case "discord-distributor": | ||||||||||||||||||||||
| return !!getConfigString(config, "channelId"); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "near-social": | ||||||||||||||||||||||
| case "near-social-distributor": | ||||||||||||||||||||||
| case "nearsocial": | ||||||||||||||||||||||
| return !!getConfigString(config, "accountId"); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| case "supabase": | ||||||||||||||||||||||
| case "supabase-distributor": | ||||||||||||||||||||||
| return !!getConfigString(config, "url"); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| default: | ||||||||||||||||||||||
| return false; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
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.
💡 Verification agent
🧩 Analysis chain
Verify Discord channel URL format compatibility.
The Discord URL format using
@memay not work for all channel types. Server channels typically require the actual server ID.Please verify that Discord channel URLs with
@mework correctly for your use case:🌐 Web query:
💡 Result:
Yes, Discord supports channel URLs in the format
https://discord.com/channels/@me/channelIdfor direct message (DM) channels. This URL structure allows users to access their private conversations directly. For other channel types, such as text or voice channels within a server (guild), the URL format ishttps://discord.com/channels/guildId/channelId. This distinction ensures that users can navigate to both server-based and direct message channels using appropriate URLs.Ensure correct Discord channel URL generation for DMs and guild channels
We verified that
https://discord.com/channels/@me/<channelId>only works for direct‐message channels, whereas server (guild) channels require the guild ID in the URL. If this utility may be used for guild channels, update it to accept an optionalguildIdand generate the URL accordingly.• apps/app/src/lib/distributor-urls.ts: adjust return value
• Introduce an optional
guildIdparameter (or pull it from context)Suggested diff:
🤖 Prompt for AI Agents