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 @@
+
+
+
+
+
+
AuroraEditor Contributors Bot
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+> 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