import { Client, TextChannel, GatewayIntentBits, ChannelType, } from "discord.js";
import { getEnvironmentVariable } from "@langchain/core/utils/env";
import { Tool } from "@langchain/core/tools";
/**
 * A tool for retrieving messages from a discord channel using a bot.
 * It extends the base Tool class and implements the _call method to
 * perform the retrieve operation. Requires an bot token which can be set
 * in the environment variables, and a limit on how many messages to retrieve.
 * The _call method takes the discord channel ID as the input argument.
 * The bot must have read permissions to the given channel. It returns the
 * message content, author, and time the message was created for each message.
 */
export class DiscordGetMessagesTool extends Tool {
    static lc_name() {
        return "DiscordGetMessagesTool";
    }
    constructor(fields) {
        super();
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "discord-get-messages"
        });
        Object.defineProperty(this, "description", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: `A discord tool. useful for reading messages from a discord channel. 
  Input should be the discord channel ID. The bot should have read 
  permissions for the channel.`
        });
        Object.defineProperty(this, "botToken", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "messageLimit", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "client", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        const { botToken = getEnvironmentVariable("DISCORD_BOT_TOKEN"), messageLimit = 10, client, } = fields ?? {};
        if (!botToken) {
            throw new Error("Environment variable DISCORD_BOT_TOKEN missing, but is required for DiscordGetMessagesTool.");
        }
        this.client =
            client ??
                new Client({
                    intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
                });
        this.botToken = botToken;
        this.messageLimit = messageLimit;
    }
    /** @ignore */
    async _call(input) {
        try {
            await this.client.login(this.botToken);
            const channel = (await this.client.channels.fetch(input));
            if (!channel) {
                return "Channel not found.";
            }
            const messages = await channel.messages.fetch({
                limit: this.messageLimit,
            });
            await this.client.destroy();
            const results = messages.map((message) => ({
                author: message.author.tag,
                content: message.content,
                timestamp: message.createdAt,
            })) ?? [];
            return JSON.stringify(results);
        }
        catch (err) {
            await this.client.destroy();
            return "Error getting messages.";
        }
    }
}
/**
 * A tool for retrieving all servers a bot is a member of. It extends the
 * base `Tool` class and implements the `_call` method to perform the retrieve
 * operation. Requires a bot token which can be set in the environment
 * variables.
 */
export class DiscordGetGuildsTool extends Tool {
    static lc_name() {
        return "DiscordGetGuildsTool";
    }
    constructor(fields) {
        super();
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "discord-get-guilds"
        });
        Object.defineProperty(this, "description", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: `A discord tool. Useful for getting a list of all servers/guilds the bot is a member of. No input required.`
        });
        Object.defineProperty(this, "botToken", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "client", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        const { botToken = getEnvironmentVariable("DISCORD_BOT_TOKEN"), client } = fields ?? {};
        if (!botToken) {
            throw new Error("Environment variable DISCORD_BOT_TOKEN missing, but is required for DiscordGetGuildsTool.");
        }
        this.client =
            client ??
                new Client({
                    intents: [GatewayIntentBits.Guilds],
                });
        this.botToken = botToken;
    }
    /** @ignore */
    async _call(_input) {
        try {
            await this.client.login(this.botToken);
            const guilds = await this.client.guilds.fetch();
            await this.client.destroy();
            const results = guilds.map((guild) => ({
                id: guild.id,
                name: guild.name,
                createdAt: guild.createdAt,
            })) ?? [];
            return JSON.stringify(results);
        }
        catch (err) {
            await this.client.destroy();
            return "Error getting guilds.";
        }
    }
}
/**
 * A tool for retrieving text channels within a server/guild a bot is a member
 * of. It extends the base `Tool` class and implements the `_call` method to
 * perform the retrieve operation. Requires a bot token which can be set in
 * the environment variables. The `_call` method takes a server/guild ID
 * to get its text channels.
 */
export class DiscordGetTextChannelsTool extends Tool {
    static lc_name() {
        return "DiscordGetTextChannelsTool";
    }
    constructor(fields) {
        super();
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "discord-get-text-channels"
        });
        Object.defineProperty(this, "description", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: `A discord tool. Useful for getting a list of all text channels in a server/guild. Input should be a discord server/guild ID.`
        });
        Object.defineProperty(this, "botToken", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "client", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        const { botToken = getEnvironmentVariable("DISCORD_BOT_TOKEN"), client } = fields ?? {};
        if (!botToken) {
            throw new Error("Environment variable DISCORD_BOT_TOKEN missing, but is required for DiscordGetTextChannelsTool.");
        }
        this.client =
            client ??
                new Client({
                    intents: [GatewayIntentBits.Guilds],
                });
        this.botToken = botToken;
    }
    /** @ignore */
    async _call(input) {
        try {
            await this.client.login(this.botToken);
            const guild = await this.client.guilds.fetch(input);
            const channels = await guild.channels.fetch();
            await this.client.destroy();
            const results = channels
                .filter((channel) => channel?.type === ChannelType.GuildText)
                .map((channel) => ({
                id: channel?.id,
                name: channel?.name,
                createdAt: channel?.createdAt,
            })) ?? [];
            return JSON.stringify(results);
        }
        catch (err) {
            await this.client.destroy();
            return "Error getting text channels.";
        }
    }
}
/**
 * A tool for sending messages to a discord channel using a bot.
 * It extends the base Tool class and implements the _call method to
 * perform the retrieve operation. Requires a bot token and channelId which can be set
 * in the environment variables. The _call method takes the message to be
 * sent as the input argument.
 */
export class DiscordSendMessagesTool extends Tool {
    static lc_name() {
        return "DiscordSendMessagesTool";
    }
    constructor(fields) {
        super();
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "discord-send-messages"
        });
        Object.defineProperty(this, "description", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: `A discord tool useful for sending messages to a discod channel.
  Input should be the discord channel message, since we will already have the channel ID.`
        });
        Object.defineProperty(this, "botToken", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "channelId", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "client", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        const { botToken = getEnvironmentVariable("DISCORD_BOT_TOKEN"), channelId = getEnvironmentVariable("DISCORD_CHANNEL_ID"), client, } = fields ?? {};
        if (!botToken) {
            throw new Error("Environment variable DISCORD_BOT_TOKEN missing, but is required for DiscordSendMessagesTool.");
        }
        if (!channelId) {
            throw new Error("Environment variable DISCORD_CHANNEL_ID missing, but is required for DiscordSendMessagesTool.");
        }
        this.client =
            client ??
                new Client({
                    intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
                });
        this.botToken = botToken;
        this.channelId = channelId;
    }
    /** @ignore */
    async _call(message) {
        try {
            await this.client.login(this.botToken);
            const channel = (await this.client.channels.fetch(this.channelId));
            if (!channel) {
                throw new Error("Channel not found");
            }
            if (!(channel.constructor === TextChannel)) {
                throw new Error("Channel is not text channel, cannot send message");
            }
            await channel.send(message);
            await this.client.destroy();
            return "Message sent successfully.";
        }
        catch (err) {
            await this.client.destroy();
            return "Error sending message.";
        }
    }
}
/**
 * A tool for searching for messages within a discord channel using a bot.
 * It extends the base Tool class and implements the _call method to
 * perform the retrieve operation. Requires an bot token which can be set
 * in the environment variables, and the discord channel ID of the channel.
 * The _call method takes the search term as the input argument.
 * The bot must have read permissions to the given channel. It returns the
 * message content, author, and time the message was created for each message.
 */
export class DiscordChannelSearchTool extends Tool {
    static lc_name() {
        return "DiscordChannelSearchTool";
    }
    constructor(fields) {
        super();
        Object.defineProperty(this, "name", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: "discord_channel_search_tool"
        });
        Object.defineProperty(this, "description", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: `A discord toolkit. Useful for searching for messages 
  within a discord channel. Input should be the search term. The bot 
  should have read permissions for the channel.`
        });
        Object.defineProperty(this, "botToken", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "channelId", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "client", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        const { botToken = getEnvironmentVariable("DISCORD_BOT_TOKEN"), channelId = getEnvironmentVariable("DISCORD_CHANNEL_ID"), client, } = fields ?? {};
        if (!botToken) {
            throw new Error("Environment variable DISCORD_BOT_TOKEN missing, but is required for DiscordChannelSearchTool.");
        }
        if (!channelId) {
            throw new Error("Environment variable DISCORD_CHANNEL_ID missing, but is required for DiscordChannelSearchTool.");
        }
        this.client =
            client ??
                new Client({
                    intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
                });
        this.botToken = botToken;
        this.channelId = channelId;
    }
    /** @ignore */
    async _call(searchTerm) {
        try {
            await this.client.login(this.botToken);
            const channel = (await this.client.channels.fetch(this.channelId));
            if (!channel) {
                return "Channel not found";
            }
            const messages = await channel.messages.fetch();
            await this.client.destroy();
            const filtered = messages.filter((message) => message.content.toLowerCase().includes(searchTerm.toLowerCase()));
            const results = filtered.map((message) => ({
                author: message.author.tag,
                content: message.content,
                timestamp: message.createdAt,
            })) ?? [];
            return JSON.stringify(results);
        }
        catch (err) {
            await this.client.destroy();
            return "Error searching through channel.";
        }
    }
}
