diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..3acef01 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# All paths default to 'scripts' which contains all script members +* @AuroraEditor/scripts \ No newline at end of file diff --git a/.github/workflows/update_contributors.yml b/.github/workflows/update_contributors.yml new file mode 100644 index 0000000..6670a77 --- /dev/null +++ b/.github/workflows/update_contributors.yml @@ -0,0 +1,44 @@ +name: Update Contributors List +on: + schedule: + # Run every day of the week at 6am + - cron: '0 6 * * *' + workflow_dispatch: + push: + branches: + - main + +jobs: + UpdateContributors: + if: github.repository_owner == 'AuroraEditor' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Create config.json + env: + TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + echo "{ + \"url\": \"https://api.github.com/users/AuroraEditor/repos\", + \"token\": \"$TOKEN\", + \"exclude\": [ + \"ImgBotApp\", + \"aurora-care-bear\", + \"github-actions[bot]\", + \"dependabot[bot]\", + \"allcontributors[bot]\", + \"actions-user\" + ] + }" > config.json + + - name: Run & Send + run: swift application.swift + + - name: Upload 'contributors.json' to repo + run: | + git config --global user.name 'aurora-care-bear' + git config --global user.email 'aurora-care-bear@users.noreply.github.com' + git remote set-url --push origin https://aurora-care-bear:$BOT_TOKEN@github.com/AuroraEditor/AuroraEditorDiscordBot + git add contributors.json + git commit -m "Update Contributors (`date`)" + git push origin HEAD:main \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4c761a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +config.ini +config.json \ No newline at end of file diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..fcc68d8 --- /dev/null +++ b/LICENCE @@ -0,0 +1,22 @@ +### MIT License + +Copyright (C) 2024 Aurora Company + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c28042 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +

+ Logo +

+ +

+

AuroraEditor Contributors Bot

+

+ +

+ + Twitter Follow + + + Discord + + +

+ +
+ +> This is the repository for the Aurora Editor Contributors list. + +## Run + +create a config.json file in the root directory with the following contents: + +```json +{ + "url": "https://api.github.com/users/AuroraEditor/repos", + "token": "GITHUB_TOKEN", + "exclude": [ + "ImgBotApp", + "aurora-care-bear", + "github-actions[bot]", + "dependabot[bot]", + "allcontributors[bot]", + "actions-user" + ] +} +``` + +> **Note**\ +> Change `GITHUB_TOKEN` to your own values. + +Run it using: + +```bash +swift application.swift +``` \ No newline at end of file diff --git a/application.swift b/application.swift new file mode 100644 index 0000000..8a28f69 --- /dev/null +++ b/application.swift @@ -0,0 +1,170 @@ +import Foundation + +#if canImport(FoundationNetworking) +// Support network calls in Linux. +import FoundationNetworking +#endif + +/// Define the Contributor structure. +/// +/// This structure will hold the information about the contributors. +/// +/// Note: This is a class because of the need to update the contributions. +/// +/// - Parameters: +/// - login: The GitHub login of the contributor. +/// - avatar_url: The URL of the avatar of the contributor. +/// - contributions: The number of contributions of the contributor. +class Contributor: Codable { + let login: String + let avatar_url: String + var contributions: Int + + init(login: String, avatar_url: String, contributions: Int) { + self.login = login + self.avatar_url = avatar_url + self.contributions = contributions + } +} + +struct Configuration: Codable { + let url: String + let token: String + let exclude: [String] +} + +struct GitHubRepo: Codable { + let name: String + let contributors_url: String +} + +// Fail if the config file doesn't exist. +if !FileManager.default.fileExists(atPath: "config.json") { + print("Config file not found.") + exit(1) +} + +// Load the configuration. +let configuration = try JSONDecoder().decode( + Configuration.self, + from: Data(contentsOf: URL(fileURLWithPath: "config.json")) +) + +var contributors: [Contributor] = [] + +// Load the data from GitHub. +if let githubData: [GitHubRepo] = fetchData(url: configuration.url) { + + // Walk through the repos. + for repo in githubData { + // Parse the repo. + print("Parsing \(repo.name)") + parseRepo(repo: repo) + } +} else { + print("Unable to parse the data from \(configuration.url)") +} + +func parseRepo(repo: GitHubRepo) { + // Get the contributors. + + guard let contributorsInRepo: [Contributor]? = fetchData( + url: repo.contributors_url + ) else { + print("Unable to parse the data from \(repo.contributors_url)") + return + } + + // Walk through the contributors. + for contributor in contributorsInRepo ?? [] + where configuration.exclude.contains(contributor.login) == false { + // Check if the contributor is already in the list. + if let selectedContributor = contributors.first(where: { + $0.login == contributor.login + }) { + // Update the contributions. + selectedContributor.contributions += contributor.contributions + } else { + // Add the contributor. + contributors.append(.init( + login: contributor.login, + avatar_url: contributor.avatar_url, + contributions: contributor.contributions + )) + } + } +} + + +print("Contributors:") +dump(contributors) + +// Encode the contributors to JSON. +if let jsonData = try? JSONEncoder().encode(contributors) { + // Write the data to a file. + try? jsonData.write(to: URL(fileURLWithPath: "contributors.json")) +} + +// WARNING: BAD PRACTICE, I'M FORCING THE PROGRAM TO WAIT FOR THE DATA. +func fetchData(url fromURL: String) -> T? { + guard let url = URL(string: fromURL) else { + print("Invalid URL") + return nil + } + + var wait = true + var data: Data? + + var request = URLRequest(url: url) + request.setValue("en", forHTTPHeaderField: "Accept-language") + request.setValue("Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us)", forHTTPHeaderField: "User-Agent") + + if configuration.token != "" { + request.setValue("Bearer \(configuration.token)", forHTTPHeaderField: "Authorization") + } + + request.httpMethod = "GET" + + let task = URLSession.shared.dataTask(with: request) { ddata, response, error in + guard + let ddata = ddata, + let response = response as? HTTPURLResponse, + error == nil + else { + print("HTTP ERROR") + print(error!.localizedDescription) + wait = false + return + } + + guard (200 ... 299) ~= response.statusCode else { + print("statusCode should be 2xx, but is \(response.statusCode)") + print("response = \(response)") + wait = false + return + } + + data = ddata + wait = false + } + + task.resume() + + while (wait) { } + + do { + let json = try JSONDecoder().decode( + T.self, + from: data! + ) + + return json + } catch { + print(error) + } + + return nil +} + +// Tell the OS that the program exited successfully. +exit(0) diff --git a/config.sample.json b/config.sample.json new file mode 100644 index 0000000..3d26a1a --- /dev/null +++ b/config.sample.json @@ -0,0 +1,12 @@ +{ + "url": "https://api.github.com/users/AuroraEditor/repos", + "token": "GITHUB_TOKEN", + "exclude": [ + "ImgBotApp", + "aurora-care-bear", + "github-actions[bot]", + "dependabot[bot]", + "allcontributors[bot]", + "actions-user" + ] +} \ No newline at end of file diff --git a/contributors.json b/contributors.json new file mode 100644 index 0000000..c269b3a --- /dev/null +++ b/contributors.json @@ -0,0 +1,182 @@ +[ + { + "contributions": 1630, + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/1290461?v=4", + "login": "0xWDG" + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/17158860?v=4", + "contributions": 126, + "login": "pkasila" + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/63672227?v=4", + "login": "nanashili", + "contributions": 706 + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/9460130?v=4", + "contributions": 565, + "login": "lukepistrol" + }, + { + "login": "KaiTheRedNinja", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/88234730?v=4", + "contributions": 452 + }, + { + "contributions": 100, + "login": "austincondiff", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/806104?v=4" + }, + { + "contributions": 51, + "login": "lilingxi01", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/36816148?v=4" + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/20476002?v=4", + "login": "Angelk90", + "contributions": 58 + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/22616933?v=4", + "login": "RayZhao1998", + "contributions": 23 + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/9656572?v=4", + "contributions": 23, + "login": "MarcoCarnevali" + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/11800807?v=4", + "contributions": 18, + "login": "StefKors" + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/8013017?v=4", + "login": "richardtop", + "contributions": 16 + }, + { + "login": "thecoolwinter", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/35942988?v=4", + "contributions": 13 + }, + { + "login": "drucelweisse", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/36012972?v=4", + "contributions": 12 + }, + { + "contributions": 11, + "login": "mmshivesh", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/23611514?v=4" + }, + { + "contributions": 10, + "login": "Jeehut", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/6942160?v=4" + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/7561070?v=4", + "contributions": 10, + "login": "Debdut" + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/43755491?v=4", + "login": "MysteryCoder456", + "contributions": 5 + }, + { + "login": "mithem", + "contributions": 5, + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/41842729?v=4" + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/3182119?v=4", + "contributions": 4, + "login": "ladvoc" + }, + { + "login": "dzign1", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/44317715?v=4", + "contributions": 4 + }, + { + "login": "avdept", + "contributions": 4, + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/1757017?v=4" + }, + { + "login": "akring", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/6525286?v=4", + "contributions": 3 + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/31246956?v=4", + "contributions": 3, + "login": "zuziakaxel" + }, + { + "contributions": 3, + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/5498199?v=4", + "login": "henryhchchc" + }, + { + "login": "jasonplatts", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/48892071?v=4", + "contributions": 3 + }, + { + "contributions": 3, + "login": "Maartz", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/13407086?v=4" + }, + { + "login": "mishapark", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/66741910?v=4", + "contributions": 3 + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/43724855?v=4", + "contributions": 3, + "login": "Kyle-Ye" + }, + { + "contributions": 11, + "login": "rishaandesai", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/77017806?v=4" + }, + { + "contributions": 4, + "login": "briangor", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/27485722?v=4" + }, + { + "login": "jenslys", + "contributions": 3, + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/69081683?v=4" + }, + { + "login": "Lakhwinderr", + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/39651639?v=4", + "contributions": 1 + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/61287791?v=4", + "login": "Sumonta056", + "contributions": 1 + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/64256342?v=4", + "login": "code-reaper08", + "contributions": 1 + }, + { + "avatar_url": "https:\/\/avatars.githubusercontent.com\/u\/16967687?v=4", + "login": "hkamran80", + "contributions": 1 + } +] \ No newline at end of file