index.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. require('dotenv').config();
  2. const axios = require('axios');
  3. const BasePlatformService = require('./utils/BasePlatformService');
  4. const { getDb } = require('./utils/MongoDBConnector');
  5. const GRAPH_API = 'https://graph.facebook.com/v22.0';
  6. class FacebookService extends BasePlatformService {
  7. constructor() {
  8. super('facebook');
  9. }
  10. // Read selected Facebook Pages from MongoDB.
  11. // Falls back to env vars for backwards compatibility.
  12. async _getPages() {
  13. try {
  14. const db = await getDb();
  15. const cred = await db.collection('platform_credentials').findOne({ _id: 'facebook' });
  16. const dbPages = (cred?.pages || []).filter((p) => p.selected);
  17. if (dbPages.length > 0) return dbPages;
  18. } catch (_) { /* fall through */ }
  19. // Env var fallback (legacy single-page mode)
  20. const { FACEBOOK_PAGE_ACCESS_TOKEN, FACEBOOK_PAGE_ID } = process.env;
  21. if (FACEBOOK_PAGE_ACCESS_TOKEN && FACEBOOK_PAGE_ID) {
  22. return [{ id: FACEBOOK_PAGE_ID, accessToken: FACEBOOK_PAGE_ACCESS_TOKEN }];
  23. }
  24. return [];
  25. }
  26. async getStatus() {
  27. const pages = await this._getPages();
  28. if (pages.length === 0) {
  29. return { connected: false, platform: 'facebook', error: 'No Facebook Pages connected — use Settings to connect via Facebook OAuth' };
  30. }
  31. try {
  32. const first = pages[0];
  33. const res = await axios.get(`${GRAPH_API}/${first.id}`, {
  34. params: {
  35. fields: 'id,name,username,picture',
  36. access_token: first.accessToken,
  37. },
  38. });
  39. return {
  40. connected: true,
  41. platform: 'facebook',
  42. username: res.data.username || res.data.name,
  43. displayName: res.data.name,
  44. avatar: res.data.picture?.data?.url,
  45. pageCount: pages.length,
  46. };
  47. } catch (err) {
  48. return { connected: false, platform: 'facebook', error: err.response?.data?.error?.message || err.message };
  49. }
  50. }
  51. async fetchFeed({ limit = 20 } = {}) {
  52. const pages = await this._getPages();
  53. if (pages.length === 0) throw new Error('No Facebook Pages connected');
  54. const allItems = [];
  55. for (const page of pages) {
  56. const res = await axios.get(`${GRAPH_API}/${page.id}/feed`, {
  57. params: {
  58. fields: 'id,message,story,full_picture,created_time,permalink_url,likes.summary(true),comments.summary(true),shares',
  59. limit: Math.min(Number(limit), 100),
  60. access_token: page.accessToken,
  61. },
  62. });
  63. const items = (res.data.data || []).map((post) =>
  64. this.normalizeFeedItem({
  65. originalId: post.id,
  66. author: {
  67. name: page.name || '',
  68. username: page.name || '',
  69. },
  70. content: post.message || post.story || '',
  71. media: post.full_picture ? [{ url: post.full_picture, type: 'image' }] : [],
  72. metrics: {
  73. likes: post.likes?.summary?.total_count || 0,
  74. comments: post.comments?.summary?.total_count || 0,
  75. shares: post.shares?.count || 0,
  76. },
  77. url: post.permalink_url,
  78. createdAt: post.created_time,
  79. })
  80. );
  81. allItems.push(...items);
  82. }
  83. try {
  84. const db = await getDb();
  85. const col = db.collection('feeds');
  86. for (const item of allItems) {
  87. await col.updateOne(
  88. { platform: 'facebook', originalId: item.originalId },
  89. { $set: item },
  90. { upsert: true }
  91. );
  92. }
  93. } catch (err) {
  94. this.app.log.error({ action: 'feed_write', platform: 'facebook', outcome: 'failure', err: err.message });
  95. }
  96. return allItems;
  97. }
  98. async publishPost({ content, link, imageUrl, accountId } = {}) {
  99. const allPages = await this._getPages();
  100. if (allPages.length === 0) throw new Error('No Facebook Pages connected');
  101. if (!content) throw new Error('content is required');
  102. // If a specific page is requested, target only that page
  103. const pages = accountId ? allPages.filter((p) => p.id === accountId) : allPages;
  104. if (pages.length === 0) throw new Error(`Facebook page ${accountId} not found or not connected`);
  105. const results = [];
  106. for (const page of pages) {
  107. const params = { message: content, access_token: page.accessToken };
  108. if (link) params.link = link;
  109. if (imageUrl) params.picture = imageUrl;
  110. const res = await axios.post(`${GRAPH_API}/${page.id}/feed`, null, { params });
  111. results.push({ pageId: page.id, pageName: page.name, postId: res.data.id });
  112. }
  113. return results;
  114. }
  115. }
  116. const service = new FacebookService();
  117. service.start(process.env.PORT || 3006);