index.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. require('dotenv').config();
  2. const { createRestAPIClient } = require('masto');
  3. const BasePlatformService = require('./utils/BasePlatformService');
  4. const { getDb } = require('./utils/MongoDBConnector');
  5. const MASTODON_INSTANCE_URL = process.env.MASTODON_INSTANCE_URL || 'https://mastodon.social';
  6. const MASTODON_ACCESS_TOKEN = process.env.MASTODON_ACCESS_TOKEN || '';
  7. class MastodonService extends BasePlatformService {
  8. constructor() {
  9. super('mastodon');
  10. this.client = null;
  11. }
  12. _initClient() {
  13. if (!MASTODON_ACCESS_TOKEN) return null;
  14. return createRestAPIClient({
  15. url: MASTODON_INSTANCE_URL,
  16. accessToken: MASTODON_ACCESS_TOKEN,
  17. });
  18. }
  19. async getStatus() {
  20. if (!MASTODON_ACCESS_TOKEN) {
  21. return { connected: false, platform: 'mastodon', error: 'Access token not configured' };
  22. }
  23. try {
  24. const client = this._initClient();
  25. const account = await client.v1.accounts.verifyCredentials();
  26. return {
  27. connected: true,
  28. platform: 'mastodon',
  29. username: account.username,
  30. displayName: account.displayName,
  31. avatar: account.avatar,
  32. instance: MASTODON_INSTANCE_URL,
  33. };
  34. } catch (err) {
  35. return { connected: false, platform: 'mastodon', error: err.message };
  36. }
  37. }
  38. async fetchFeed({ limit = 40 } = {}) {
  39. const client = this._initClient();
  40. if (!client) throw new Error('Mastodon access token not configured');
  41. const statuses = await client.v1.timelines.home.list({ limit: Number(limit) });
  42. const items = statuses.map((status) =>
  43. this.normalizeFeedItem({
  44. originalId: status.id,
  45. author: {
  46. name: status.account.displayName || status.account.username,
  47. username: `${status.account.username}@${new URL(MASTODON_INSTANCE_URL).hostname}`,
  48. avatar: status.account.avatar,
  49. profileUrl: status.account.url,
  50. },
  51. content: status.text || _stripHtml(status.content),
  52. contentHtml: status.content,
  53. media: (status.mediaAttachments || []).map((m) => ({
  54. url: m.url,
  55. type: m.type,
  56. thumbnail: m.previewUrl,
  57. alt: m.description,
  58. })),
  59. platformTags: (status.tags || []).map((t) => t.name),
  60. metrics: {
  61. likes: status.favouritesCount,
  62. shares: status.reblogsCount,
  63. comments: status.repliesCount,
  64. },
  65. url: status.url,
  66. createdAt: status.createdAt,
  67. })
  68. );
  69. // MongoDB'ye kaydet (upsert)
  70. try {
  71. const db = await getDb();
  72. const col = db.collection('feeds');
  73. for (const item of items) {
  74. await col.updateOne(
  75. { platform: 'mastodon', originalId: item.originalId },
  76. { $set: item },
  77. { upsert: true }
  78. );
  79. }
  80. } catch (err) {
  81. this.app.log.error({ action: 'feed_write', platform: 'mastodon', outcome: 'failure', err: err.message });
  82. }
  83. return items;
  84. }
  85. async publishPost({ content, media = [], sensitive = false, spoilerText = '', firstComment } = {}) {
  86. const client = this._initClient();
  87. if (!client) throw new Error('Mastodon access token not configured');
  88. const status = await client.v1.statuses.create({
  89. status: content,
  90. sensitive,
  91. spoilerText,
  92. mediaIds: media,
  93. });
  94. if (firstComment?.trim()) {
  95. try {
  96. await client.v1.statuses.create({
  97. status: firstComment.trim(),
  98. inReplyToId: status.id,
  99. });
  100. this.app.log.info({ action: 'first_comment', platform: 'mastodon', statusId: status.id, outcome: 'success' });
  101. } catch (err) {
  102. this.app.log.warn({ action: 'first_comment', platform: 'mastodon', statusId: status.id, outcome: 'failure', err: err.message });
  103. }
  104. }
  105. return { id: status.id, url: status.url };
  106. }
  107. }
  108. function _stripHtml(html = '') {
  109. return html.replace(/<[^>]+>/g, '').trim();
  110. }
  111. const service = new MastodonService();
  112. service.start(process.env.PORT || 3003);