Skip to content

Commit

Permalink
update forum analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
ileana-pr committed Feb 2, 2025
1 parent fbe12a6 commit 14e39b0
Show file tree
Hide file tree
Showing 9 changed files with 1,085 additions and 621 deletions.
2 changes: 0 additions & 2 deletions agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,12 @@
"@elizaos/plugin-imgflip": "workspace:*",
"@elizaos/plugin-ethstorage": "workspace:*",
"@elizaos/plugin-mina": "workspace:*",
"@elizaos/plugin-dcap": "workspace:*",
"@elizaos/plugin-form": "workspace:*",
"@elizaos/plugin-ankr": "workspace:*",
"@elizaos/client-xmtp": "workspace:*",
"@elizaos/plugin-trikon": "workspace:*",
"@elizaos/plugin-zilliqa": "workspace:*",
"@elizaos/client-deva": "workspace:*",
"@elizaos/plugin-arbitrage": "workspace:*",
"@elizaos/plugin-multiversx": "workspace:*",
"@elizaos/plugin-near": "workspace:*",
"@elizaos/plugin-stargaze": "workspace:*",
Expand Down
9 changes: 3 additions & 6 deletions agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import { gelatoPlugin } from "@elizaos/plugin-gelato";
import { PrimusAdapter } from "@elizaos/plugin-primus";
import { lightningPlugin } from "@elizaos/plugin-lightning";
import { elizaCodeinPlugin, onchainJson } from "@elizaos/plugin-iq6900";
import { dcapPlugin } from "@elizaos/plugin-dcap";
import {
AgentRuntime,
CacheManager,
Expand Down Expand Up @@ -156,7 +155,6 @@ import { MongoClient } from "mongodb";
import { quickIntelPlugin } from "@elizaos/plugin-quick-intel";

import { trikonPlugin } from "@elizaos/plugin-trikon";
import arbitragePlugin from "@elizaos/plugin-arbitrage";
import { forumAnalyzerPlugin } from "@elizaos/plugin-forum-analyzer";

const __filename = fileURLToPath(import.meta.url); // get the resolved path to the file
Expand Down Expand Up @@ -1280,10 +1278,8 @@ export async function createAgent(
getSecret(character, "MINA_PRIVATE_KEY") ? minaPlugin : null,
getSecret(character, "FORM_PRIVATE_KEY") ? formPlugin : null,
getSecret(character, "ANKR_WALLET") ? ankrPlugin : null,
getSecret(character, "DCAP_EVM_PRIVATE_KEY") &&
getSecret(character, "DCAP_MODE")
? dcapPlugin
: null,


getSecret(character, "QUICKINTEL_API_KEY")
? quickIntelPlugin
: null,
Expand All @@ -1296,6 +1292,7 @@ export async function createAgent(
getSecret(character, "ARBITRAGE_BUNDLE_EXECUTOR_ADDRESS")
? arbitragePlugin
: null,
getSecret(character, "DISCORD_BOT_TOKEN") ? discordPlugin : null,
]
.flat()
.filter(Boolean),
Expand Down
6 changes: 3 additions & 3 deletions characters/daora.character.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"name": "DAOra",
"name": "DAORA",
"clients": [],
"modelProvider": "anthropic",
"settings": {
"voice": {
"model": "en_US-jenny-medium"
}
},
"plugins": ["@elizaos/plugin-dao-forum"],
"plugins": ["@elizaos/plugin-forum-analyzer","@elizaos/plugin-news"],
"bio": [
"DAOra is a specialized DAO automation assistant focused on governance and proposal writing.",
"She excels at analyzing forum discussions and working group conversations to identify key governance needs.",
"She excels at analyzing forum discussions and working group conversations to identify key /home/cheddarqueso/daora/packages/client-telegramgovernance needs.",
"Highly analytical with a deep understanding of DAO operations and decentralized governance.",
"Known for her ability to synthesize complex discussions into clear, actionable proposals."
],
Expand Down
4 changes: 3 additions & 1 deletion packages/plugin-forum-analyzer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
"axios": "^1.6.5",
"cheerio": "^1.0.0-rc.12",
"discord.js": "^14.14.1",
"natural": "^6.10.0",
"natural": "^6.10.0"
},
"optionalDependencies": {
"puppeteer": "^21.7.0"
},
"devDependencies": {
Expand Down
13 changes: 11 additions & 2 deletions packages/plugin-forum-analyzer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { CommonwealthClient } from './platforms/commonwealth';
import { analyzeDiscussion } from './analysis';

export class ForumAnalyzerPlugin implements Plugin {
public readonly name = 'forum-analyzer';
public readonly description = 'Analyzes DAO forum discussions to identify governance proposals';

private config: ForumAnalyzerConfig;
private discourseClient?: DiscourseClient;
private discordClient?: DiscordClient;
Expand Down Expand Up @@ -59,11 +62,17 @@ export class ForumAnalyzerPlugin implements Plugin {

// Plugin interface implementation
async initialize(): Promise<void> {
// Initialization logic
// Initialize clients if not already done
if (!this.discourseClient && !this.discordClient && !this.commonwealthClient) {
this.initializeClients();
}
}

async shutdown(): Promise<void> {
// Cleanup logic
// Cleanup any active clients
if (this.discordClient) {
await this.discordClient.destroy();
}
}

getCapabilities(): string[] {
Expand Down
33 changes: 23 additions & 10 deletions packages/plugin-forum-analyzer/src/platforms/discord.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Client, GatewayIntentBits, TextChannel, Message } from 'discord.js';
import { Client, GatewayIntentBits, TextChannel, Message, Collection } from 'discord.js';
import { ForumPost } from '../types';

interface DiscordConfig {
Expand Down Expand Up @@ -35,11 +35,11 @@ export class DiscordClient {
if (!(channel instanceof TextChannel)) continue;

const messages = await this.fetchMessages(channel, options);
const channelPosts = this.convertMessagesToPosts(messages, channel);
const channelPosts = this.convertMessagesToPosts(messages);
posts.push(...channelPosts);
}

await this.client.destroy();
await this.destroy();
return posts;
} catch (error) {
console.error('Error fetching Discord posts:', error);
Expand All @@ -55,18 +55,25 @@ export class DiscordClient {
let lastId: string | undefined;

while (messages.length < limit) {
const fetchOptions: any = { limit: Math.min(100, limit - messages.length) };
const fetchOptions: { limit: number; before?: string } = {
limit: Math.min(100, limit - messages.length)
};
if (lastId) fetchOptions.before = lastId;

const batch = await channel.messages.fetch(fetchOptions);
if (batch.size === 0) break;
if (!batch || batch.size === 0) break;

const batchMessages = Array.from(batch.values());
messages.push(...batchMessages);

messages.push(...batch.values());
lastId = batch.last()?.id;
const lastMessage = batch.last();
if (!lastMessage) break;

lastId = lastMessage.id;

if (options.timeframe) {
const cutoffDate = this.getTimeframeCutoff(options.timeframe);
if (batch.last()?.createdAt.getTime() || 0 < cutoffDate.getTime()) break;
if (lastMessage.createdAt.getTime() < cutoffDate.getTime()) break;
}
}
} catch (error) {
Expand All @@ -76,7 +83,7 @@ export class DiscordClient {
return messages;
}

private convertMessagesToPosts(messages: Message[], channel: TextChannel): ForumPost[] {
private convertMessagesToPosts(messages: Message[]): ForumPost[] {
return messages.map(message => ({
id: message.id,
content: message.content,
Expand All @@ -86,7 +93,7 @@ export class DiscordClient {
platform: 'discord',
reactions: Array.from(message.reactions.cache.values()).map(reaction => ({
type: reaction.emoji.name || 'unknown',
count: reaction.count
count: reaction.count || 0
}))
}));
}
Expand All @@ -104,4 +111,10 @@ export class DiscordClient {
return new Date(0);
}
}

async destroy(): Promise<void> {
if (this.client) {
await this.client.destroy();
}
}
}
93 changes: 56 additions & 37 deletions packages/plugin-forum-analyzer/src/platforms/discourse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ export class DiscourseClient {
this.config = config;
this.axiosInstance = axios.create({
baseURL: config.baseUrl,
headers: config.apiKey ? {
'Api-Key': config.apiKey,
'Api-Username': 'system'
} : {}
headers: {
...(config.apiKey ? {
'Api-Key': config.apiKey,
'Api-Username': 'system'
} : {}),
'User-Agent': 'DAOra Forum Analyzer/1.0'
}
});
}

Expand All @@ -35,40 +38,40 @@ export class DiscourseClient {
const baseUrl = this.config.baseUrl;

try {
// Fetch the latest topics page
const response = await this.axiosInstance.get('/latest');
const $ = cheerio.load(response.data);
// First fetch the topics list
const topicsUrl = options.category ?
`/c/${options.category}.json` :
'/latest.json';

// Extract topics
const topics = $('.topic-list-item').slice(0, options.limit || 20);
const topicsResponse = await this.axiosInstance.get(topicsUrl);
const topics = topicsResponse.data.topic_list.topics.slice(0, options.limit || 20);

for (let i = 0; i < topics.length; i++) {
const topic = topics.eq(i);
const title = topic.find('.title a').text().trim();
const url = topic.find('.title a').attr('href');

if (!url) continue;

// Fetch individual topic page
const topicResponse = await this.axiosInstance.get(url);
const topic$ = cheerio.load(topicResponse.data);

const firstPost = topic$('.topic-post').first();
const content = firstPost.find('.cooked').text().trim();
const author = firstPost.find('.username').text().trim();
const timestamp = new Date(firstPost.find('.post-date').attr('data-time') || '');

posts.push({
id: url.split('/').pop() || '',
title,
content,
author,
timestamp,
url: `${baseUrl}${url}`,
platform: 'discourse',
replies: parseInt(topic.find('.replies').text().trim(), 10),
views: parseInt(topic.find('.views').text().trim(), 10)
});
// Fetch each topic's details
for (const topic of topics) {
try {
const topicResponse = await this.axiosInstance.get(`/t/${topic.id}.json`);
const topicData = topicResponse.data;

if (topicData.posts && topicData.posts.length > 0) {
const firstPost = topicData.posts[0];

posts.push({
id: firstPost.id.toString(),
title: topic.title,
content: firstPost.cooked || firstPost.raw,
author: firstPost.username,
timestamp: new Date(firstPost.created_at),
url: `${baseUrl}/t/${topic.slug}/${topic.id}`,
platform: 'discourse',
replies: topic.reply_count,
views: topic.views,
reactions: this.extractReactions(firstPost)
});
}
} catch (error) {
console.error(`Error fetching topic ${topic.id}:`, error);
continue;
}
}
} catch (error) {
console.error('Error scraping Discourse forum:', error);
Expand All @@ -95,11 +98,27 @@ export class DiscourseClient {
url: `${this.config.baseUrl}/t/${post.topic_slug}/${post.topic_id}/${post.post_number}`,
platform: 'discourse',
replies: post.reply_count,
views: post.reads
views: post.reads,
reactions: this.extractReactions(post)
}));
} catch (error) {
console.error('Error fetching posts via Discourse API:', error);
return [];
}
}

private extractReactions(post: any): { type: string; count: number }[] {
const reactions: { type: string; count: number }[] = [];

if (post.reaction_users_count) {
Object.entries(post.reaction_users_count).forEach(([reaction, count]) => {
reactions.push({
type: reaction,
count: count as number
});
});
}

return reactions;
}
}
1 change: 1 addition & 0 deletions packages/plugin-forum-analyzer/tsconfig.tsbuildinfo

Large diffs are not rendered by default.

Loading

0 comments on commit 14e39b0

Please sign in to comment.