Skip to content

Commit

Permalink
fix: update GitHub API integration to use instance variables for repo…
Browse files Browse the repository at this point in the history
…sitory details #6

refactor: update GitHub API integration to use instance variables for repository details
  • Loading branch information
retardgerman authored Jan 6, 2025
2 parents 7a2e84c + 26b801d commit d77d2ab
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 78 deletions.
7 changes: 5 additions & 2 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module.exports = class Streamyfin extends Client {

this.commands = new Collection();
this.userId = '398161771476549654';
this.repoOwner = process.env.REPO_OWNER;
this.repoName = process.env.REPO_NAME;
this.githubToken = process.env.GITHUB_TOKEN;

}

Expand All @@ -25,10 +28,10 @@ module.exports = class Streamyfin extends Client {
async fetchReleases(){
try {
const response = await axios.get(
`${process.env.GITHUB_API_BASE}/repos/${process.env.REPO_OWNER}/${process.env.REPO_NAME}/releases`,
`${process.env.GITHUB_API_BASE}/repos/${this.repoOwner}/${this.repoName}/releases`,
{
headers: {
Authorization: `token ${process.env.GITHUB_TOKEN}`,
Authorization: `token ${this.githubToken}`,
},
}
);
Expand Down
3 changes: 1 addition & 2 deletions commands/bot/beta.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ module.exports = {
.setDescription('Explains how to join the Streamyfin Beta program.'),
async run(interaction) {
await interaction.reply(
`To access the Streamyfin beta, you need to subscribe to the Member tier (or higher) on [Patreon](https://www.patreon.com/streamyfin). This will give you immediate access to the beta channels on Discord and we´ll know that you have subscribed. This is where we will post APKs and IPAs. This won't give automatic access to the TestFlight however, so you need to send <@${interaction.client.userId}> a DM with the email you use for Apple so that he can manually add you.`

`To access the Streamyfin beta, you need to subscribe to the Member tier (or higher) on [Patreon](https://www.patreon.com/streamyfin). This will give you immediate access to the ⁠#🧪-public-beta channel on Discord and we´ll know that you have subscribed. This is where we will post APKs and IPAs. This won't give automatic access to the TestFlight however, so you need to send <@${interaction.client.userId}> a DM with the email you use for Apple so that he can manually add you.`
)
},
};
13 changes: 7 additions & 6 deletions commands/github/closeissue.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ module.exports = {
),
async run(interaction) {
const GITHUB_API_BASE = "https://api.github.com";
const REPO_OWNER = process.env.REPO_OWNER;
const REPO_NAME = process.env.REPO_NAME;
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const allowedRoles = ["Developer", "Administrator"];
const state = interaction.options.getString("state");
const stateReason = interaction.options.getString("state_reason");
Expand Down Expand Up @@ -66,13 +63,17 @@ module.exports = {
const issueNumber = issueUrlMatch[1];

await axios.patch(
`${GITHUB_API_BASE}/repos/${REPO_OWNER}/${REPO_NAME}/issues/${issueNumber}`,
`${GITHUB_API_BASE}/repos/${interaction.client.repoOwner}/${interaction.client.repoName}/issues/${issueNumber}`,
{ state, state_reason: stateReason },
{ headers: { Authorization: `token ${GITHUB_TOKEN}` } }
{ headers: { Authorization: `token ${interaction.client.githubToken}` } }
);

await thread.setLocked(true, "Thread closed by developer.");
await thread.send(`✅ This issue has been resolved and the GitHub issue is now "${state}" with reason "${stateReason}".`);
await interaction.reply({ content: "✅ Issue closed successfully.", ephemeral: true });
} catch (error) {
console.error("Error closing issue:", error);
await interaction.reply({ content: "❌ Failed to close the issue. Please try again.", ephemeral: true });
}
},
};
};
72 changes: 18 additions & 54 deletions commands/github/createissue.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ module.exports = {
.setName('createissue')
.setDescription('Create a new issue on GitHub.'),
async run(interaction) {
const REPO_OWNER = process.env.REPO_OWNER;
const GITHUB_API_BASE = "https://api.github.com";
const REPO_NAME = process.env.REPO_NAME;
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;

// Start by creating a private thread in forum
const forumChannelId = process.env.FORUM_CHANNEL_ID;
const forumChannel = interaction.guild.channels.cache.get(forumChannelId);
Expand Down Expand Up @@ -58,73 +56,39 @@ module.exports = {
: "No screenshots provided";

const body = `
### What happened?
${collectedData.description}
### Reproduction steps
${collectedData.steps}
### Device and operating system
${collectedData.device}
### Version
${collectedData.version}
### Screenshots
${screenshotsText}
`;

const notifyAndDeleteThread = async (thread) => {
try {
await thread.send("⚠️ This thread will be deleted in 1 minute because the issue has been closed on GitHub.");
setTimeout(async () => {
await thread.delete("Issue closed on GitHub.");
}, 60000); // Delete after 1 minute
} catch (error) {
console.error("Error notifying and deleting thread:", error);
}
};

const checkIssueClosed = async (issueNumber, thread) => {
try {
const issueResponse = await axios.get(
`${GITHUB_API_BASE}/repos/${REPO_OWNER}/${REPO_NAME}/issues/${issueNumber}`,
{
headers: {
Authorization: `token ${GITHUB_TOKEN}`,
},
}
);

if (issueResponse.data.state === "closed") {
notifyAndDeleteThread(thread);
} else {
setTimeout(() => checkIssueClosed(issueNumber, thread), 60000); // Check again in 1 minute
}
} catch (error) {
console.error("Error checking issue state:", error);
setTimeout(() => checkIssueClosed(issueNumber, thread), 60000); // Check again in 1 minute
}
};
### What happened?
${collectedData.description}
### Reproduction steps
${collectedData.steps}
### Device and operating system
${collectedData.device}
### Version
${collectedData.version}
### Screenshots
${screenshotsText}
`;

try {
const issueResponse = await axios.post(
`${GITHUB_API_BASE}/repos/${REPO_OWNER}/${REPO_NAME}/issues`,
`${GITHUB_API_BASE}/repos/${interaction.client.repoOwner}/${interaction.client.repoName}/issues`,
{
title: `[Bug]: ${collectedData.title} reported via Discord by [${interaction.user.username}]`,
body: body,
labels: ["❌ bug"],
},
{
headers: {
Authorization: `token ${GITHUB_TOKEN}`,
Authorization: `token ${interaction.client.githubToken}`,
},
}
);

await thread.send(`✅ Issue created successfully: ${issueResponse.data.html_url}`);
await thread.setLocked(true, "Issue details collected and sent to GitHub.");
checkIssueClosed(issueResponse.data.number, thread); // Start checking if the issue is closed
} catch (error) {
console.error("Error creating issue:", error);
await thread.send("❌ Failed to create the issue. Please try again.");
Expand Down
64 changes: 60 additions & 4 deletions commands/github/featurerequest.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const { SlashCommandBuilder } = require('discord.js');
const { SlashCommandBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, ButtonStyle, ButtonBuilder, ActionRowBuilder } = require('discord.js');
const axios = require('axios');

module.exports = {
data: new SlashCommandBuilder()
Expand All @@ -8,10 +9,19 @@ module.exports = {
option.setName('description')
.setDescription('The description of the feature you want to request.')
.setRequired(true)),

async run(interaction) {
const description = interaction.options.getString("description");
const targetChannel = interaction.client.channels.cache.get('1273278866105831424');
const memberRoles = interaction.member.roles.cache.map((role) => role.name);
const allowedRoles = ["Developer", "Administrator"];

const modal = new ModalBuilder().setCustomId('githubModal').setTitle('Github Username');
const usernameInput = new TextInputBuilder().setCustomId('username').setLabel('Please enter your Github username').setPlaceholder('Github Username').setStyle(TextInputStyle.Short);
const button = new ButtonBuilder().setCustomId('submit').setLabel('Submit to Github').setStyle(ButtonStyle.Success);
const row = new ActionRowBuilder().addComponents(button);

modal.addComponents(new ActionRowBuilder().addComponents(usernameInput));

if (!targetChannel) {
await interaction.reply({ content: '❌ Target channel not found.', ephemeral: true });
return;
Expand All @@ -22,7 +32,53 @@ module.exports = {
reason: 'User requested a feature',
});

await thread.send({ content: `🎉 Thank you for your feature request! Feel free to discuss this feature here!` });
await thread.send({ content: `🎉 Thank you for your feature request! Feel free to discuss this feature here!`, components: [row] });
await interaction.reply({ content: `✅ Your feature request has been submitted and a discussion thread has been created: [${thread.name}](https://discord.com/channels/${interaction.guild.id}/${targetChannel.id}/${thread.id})`, ephemeral: true });

const filter = (i) => i.user.id === interaction.user.id;
const collector = thread.createMessageComponentCollector({ filter, time: 60000 });

collector.on('collect', async i => {
if (i.customId === 'submit') {
if (!memberRoles.some((role) => allowedRoles.includes(role))) {
await i.reply({ content: `❌ <#${interaction.user.id}>, You do not have permission to submit this to github.`, ephemeral: true });
} else {
await i.showModal(modal);
}
}
});

interaction.client.on('interactionCreate', async (modalInteraction) => {
if (!modalInteraction.isModalSubmit()) return;
if (modalInteraction.customId === 'githubModal') {
const githubUsername = modalInteraction.fields.getTextInputValue('username');
const r = await axios.get(`https://api.github.com/users/${githubUsername}`);
if (!(r.data.login === githubUsername)) await modalInteraction.reply({ content: `❌ Github username ${githubUsername} is not valid.`, ephemeral: true });

try {
const response = await axios.post(
`https://api.github.com/repos/streamyfin/streamyfin/issues`,
{
title: `Feature request from Discord user ${interaction.user.username}`,
body: description,
labels: ["✨ enhancement"],
assignees: [githubUsername],
},
{
headers: {
Authorization: `token ${interaction.client.githubToken}`,
},
}
);

await interaction.reply(`✅ Feature request created successfully: ${response.data.html_url}`);
await interaction.channel.send("🔒 This channel has been locked as details have been collected and sent to GitHub.");
} catch (error) {
console.error("Error submitting feature request:", error);
await interaction.channel.send("❌ Failed to submit the feature request. Please try again.");
}

}
});
},
}
};
9 changes: 3 additions & 6 deletions commands/github/issue.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,15 @@ module.exports = {
.setRequired(false)
),
async run(interaction) {
const REPO_OWNER = process.env.REPO_OWNER;
const REPO_NAME = process.env.REPO_NAME;
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const issueNumber = interaction.options.getInteger("number");

if (issueNumber) {
try {
const response = await axios.get(
`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/issues/${issueNumber}`,
`https://api.github.com/repos/${interaction.client.repoOwner}/${interaction.client.repoName}/issues/${issueNumber}`,
{
headers: {
Authorization: `token ${GITHUB_TOKEN}`,
Authorization: `token ${interaction.client.githubToken}`,
},
}
);
Expand All @@ -37,7 +34,7 @@ module.exports = {
return;
}

const response = await axios.get(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/issues`);
const response = await axios.get(`https://api.github.com/repos/${interaction.client.repoOwner}/${interaction.client.repoName}/issues`);
if (!response.data) return interaction.reply("Please provide an issue number");

let options = [];
Expand Down
2 changes: 1 addition & 1 deletion commands/github/roadmap.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module.exports = {
.setDescription('Get the link to the GitHub roadmap.'),
async run(interaction) {
await interaction.reply(
"📌 Here is our Roadmap: <https://github.com/orgs/streamyfin/projects/3>"
"📌 Here is our Roadmap: <https://github.com/users/fredrikburmester/projects/5/views/8>"
);
},
};
4 changes: 1 addition & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ const Streamyfin = require('./client');
const { GatewayIntentBits, REST, Routes } = require ("discord.js");
const fs = require("fs");

// GitHub API base URL and repo data
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;

const tempCommands = []

Expand Down Expand Up @@ -43,7 +41,7 @@ client.on("interactionCreate", async (interaction) => {
}
})
const registerCommands = async () => {
if (GITHUB_TOKEN) await client.fetchReleases();
if (client.githubToken) await client.fetchReleases();

const rest = new REST({ version: "10" }).setToken(process.env.DISCORD_TOKEN);

Expand Down

0 comments on commit d77d2ab

Please sign in to comment.