Social Media Integration - nself-org/nchat GitHub Wiki
Comprehensive social media integration for nself-chat v0.3.0, enabling automatic importing of Twitter, Instagram, and LinkedIn posts into announcement channels.
The social media integration system allows administrators to:
- Connect Social Accounts - Link Twitter, Instagram, and LinkedIn accounts via OAuth
- Configure Integrations - Map social accounts to announcement channels
- Filter Content - Apply hashtag, keyword, and engagement filters
- Auto-Import - Automatically poll for new posts and import them
- Rich Embeds - Display posts with images, videos, and engagement stats
-
nchat_social_accounts- Stores connected social media accounts -
nchat_social_posts- Imported social media posts -
nchat_social_integrations- Maps accounts to channels with filters -
nchat_social_import_logs- Audit log of import jobs
-
TwitterClient- Twitter API v2 integration -
InstagramClient- Instagram Graph API integration -
LinkedInClient- LinkedIn API v2 integration
-
SocialAccountManager- Connect and manage social accounts -
SocialIntegrationSettings- Configure channel mappings and filters -
SocialPostHistory- View imported posts
Add to .env.local:
# Social Media API Credentials
TWITTER_CLIENT_ID=your-twitter-client-id
TWITTER_CLIENT_SECRET=your-twitter-client-secret
TWITTER_BEARER_TOKEN=your-bearer-token
INSTAGRAM_APP_ID=your-instagram-app-id
INSTAGRAM_APP_SECRET=your-instagram-app-secret
LINKEDIN_CLIENT_ID=your-linkedin-client-id
LINKEDIN_CLIENT_SECRET=your-linkedin-client-secret
# Encryption Key (generate with: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))")
SOCIAL_MEDIA_ENCRYPTION_KEY=your-encryption-key-here
# App URL (for OAuth callbacks)
NEXT_PUBLIC_APP_URL=http://localhost:3000Run the migration:
cd .backend
nself exec postgres psql -U postgres -d nself < ../migrations/012_social_media_integration.sql- Go to Twitter Developer Portal
- Create a new Project + App
- Enable OAuth 2.0 with PKCE
- Add callback URL:
http://localhost:3000/api/social/twitter/callback - Request permissions:
tweet.read,users.read,offline.access - Copy Client ID and Client Secret
- Upgrade to Essential tier ($100/month) for full access
- Go to Meta for Developers
- Create a new Facebook App (Business type)
- Add Instagram Basic Display product
- Add OAuth Redirect URI:
http://localhost:3000/api/social/instagram/callback - Connect an Instagram Business Account to a Facebook Page
- Copy App ID and App Secret
- Request permissions:
instagram_basic,instagram_content_publish,pages_read_engagement
- Go to LinkedIn Developers
- Create a new App
- Request access to Marketing Developer Platform
- Add Redirect URL:
http://localhost:3000/api/social/linkedin/callback - Request permissions:
r_liteprofile,r_emailaddress,w_member_social,r_organization_social - Copy Client ID and Client Secret
Create a cron job to poll every 5 minutes:
# Edit crontab
crontab -e
# Add this line (polls every 5 minutes)
*/5 * * * * curl -X POST http://localhost:3000/api/social/pollOr use a service like Vercel Cron, Render Cron, or EasyCron.
For production, use a job queue system (see below).
- Navigate to Admin → Social Media
- Click Connect Twitter/Instagram/LinkedIn
- Authorize the app in the OAuth flow
- Account will appear in the Connected Accounts list
- Go to Integrations tab
- Select the connected account
- Choose a channel (e.g.,
#announcements) - Configure filters:
-
Hashtags: Only import posts with specific hashtags (e.g.,
#news,#updates) -
Keywords: Only import posts containing keywords (e.g.,
"product launch") - Exclude Retweets: (Twitter) Skip retweets
- Exclude Replies: (Twitter) Skip reply tweets
- Min Engagement: Only import posts with X+ likes/shares
-
Hashtags: Only import posts with specific hashtags (e.g.,
- Click Create Integration
Click the refresh icon next to any account to manually trigger an import.
Go to Post History tab to see all imported posts and their status.
Posts are imported only if they match ALL configured filters:
- Hashtags: Post must include at least one of the specified hashtags
- Keywords: Post content must contain at least one of the keywords
- Min Engagement: Total engagement (likes + shares + comments) must be >= threshold
- Exclude Retweets: Skip if post is a retweet (Twitter only)
- Exclude Replies: Skip if post is a reply (Twitter only)
If no filters are configured, all posts are imported.
Imported posts are displayed as rich embeds with:
- Platform icon and color (Twitter blue, Instagram pink, LinkedIn blue)
- Author info (name, handle, avatar)
- Post content (text, truncated to 500 chars)
- Media (images, videos, GIFs - up to 4 displayed)
- Engagement stats (likes, retweets, shares, comments)
- Link to original post
- Timestamp ("Posted 2h ago")
The polling system (src/lib/social/poller.ts) runs every 5 minutes and:
- Fetches all active social accounts
- For each account:
- Decrypts OAuth access token
- Fetches new posts since last poll
- Saves posts to
nchat_social_posts - Checks integrations for this account
- Applies filters to each post
- Posts matching content to configured channels
- Creates rich embed messages
- Updates
last_poll_time - Logs results to
nchat_social_import_logs
OAuth access tokens are encrypted using AES-256-GCM before storage:
import { encryptToken, decryptToken } from '@/lib/social/encryption'
const encrypted = encryptToken(accessToken)
// Store in database
const decrypted = decryptToken(encrypted)
// Use for API calls- Twitter: Tokens expire after 2 hours, refreshed using refresh token
- Instagram: Long-lived tokens (60 days), refreshed automatically
- LinkedIn: Tokens expire after 60 days (no refresh token)
The system will automatically attempt to refresh tokens when they expire.
For production, replace cron with a proper job queue:
// Using Bull (Redis-based)
import Bull from 'bull'
const socialPollQueue = new Bull('social-media-poll', {
redis: process.env.REDIS_URL,
})
// Add recurring job
socialPollQueue.add(
'poll-all',
{},
{
repeat: { cron: '*/5 * * * *' }, // Every 5 minutes
}
)
// Process jobs
socialPollQueue.process('poll-all', async (job) => {
const { pollAllAccounts } = await import('@/lib/social/poller')
const apolloClient = getApolloClient()
return await pollAllAccounts(apolloClient)
})API rate limits:
- Twitter: 300 requests per 15-min window (Essential tier)
- Instagram: 200 requests per hour
- LinkedIn: Varies by endpoint
The clients handle rate limits with exponential backoff.
Check import logs:
SELECT *
FROM nchat_social_import_logs
ORDER BY started_at DESC
LIMIT 10;Monitor for:
- High error rates
- Token expiration
- Failed imports
- Rate limit hits
| Endpoint | Method | Description |
|---|---|---|
/api/social/twitter/auth |
GET | Start Twitter OAuth |
/api/social/twitter/callback |
GET | Twitter OAuth callback |
/api/social/instagram/auth |
GET | Start Instagram OAuth |
/api/social/instagram/callback |
GET | Instagram OAuth callback |
/api/social/linkedin/auth |
GET | Start LinkedIn OAuth |
/api/social/linkedin/callback |
GET | LinkedIn OAuth callback |
/api/social/accounts |
GET | List all accounts |
/api/social/accounts |
POST | Create account |
/api/social/accounts |
DELETE | Delete account |
/api/social/poll |
POST | Trigger import (manual or all) |
/api/social/poll |
GET | Health check |
"Invalid redirect URI"
- Ensure callback URLs are added in developer portal
- Check
NEXT_PUBLIC_APP_URLmatches registered URL
"Insufficient permissions"
- Request additional scopes in developer portal
- Re-authorize the account
"Failed to fetch posts"
- Check access token validity
- Verify API credentials
- Check rate limits
"No posts imported"
- Verify filters aren't too restrictive
- Check if account has recent posts
- Ensure account is active
Tokens will automatically refresh if:
- Refresh token is available (Twitter)
- Token hasn't fully expired (Instagram)
If refresh fails, admin must re-authenticate:
- Go to Admin → Social Media
- Click account
- Click "Reconnect"
- Webhook support (real-time imports)
- Multi-account posting (post to social from chat)
- Sentiment analysis on imported posts
- TikTok integration
- YouTube integration
- Reddit integration
- Advanced analytics dashboard
- Scheduled posting
- Draft management
| Service | Tier | Cost |
|---|---|---|
| Twitter API | Essential | $100/month |
| Instagram API | Free | $0 |
| LinkedIn API | Free | $0 |
| Total | $100/month |
Note: Twitter is the only paid service. Instagram and LinkedIn APIs are free for standard usage.
For issues or questions:
- Check logs in
nchat_social_import_logs - Review Sentry errors (if enabled)
- Contact nself-chat support