├── .dockerignore
├── .env
├── .envrc.example
├── .github
├── PULL_REQUEST_TEMPLATE.md
├── logo.svg
├── renovate.json
└── workflows
│ ├── audit.yml
│ ├── cd.yml
│ ├── commands.yml
│ └── test.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .sqlx
├── query-07ca64b10025a77a597f7d003c7bf4fdd0b1a7a9b23f9246c6dab120a91f0498.json
├── query-0b5aa446aa706d7d3b4f4e485d589f2c116d529b46e74f3440201d08b6c56bf4.json
├── query-10fd2084c314c5a357f7408746702d90c908a44c3148b03de91bcf5eaee9712e.json
├── query-14c35efbbe2e0c6c2edcec300f4f9f58257c5bf84a28f1ac2156539edc3ba6e0.json
├── query-1a7ad5f985960145009f82ab41eda810cf2f14b510e5196aaf6684d945daf017.json
├── query-2971ad0c94858b6c33d6ba863ea2e877fd574267d806ed1f7593c5cbb14ba1a2.json
├── query-2a9879a85acc57d9a732657e57494dfb60c75f544f90e0c328e8c9bc34901f8e.json
├── query-34c16baa9bad362981992f30483bf07b6af29662e693aad5f5ec31620b11c1a9.json
├── query-3b3217f05ec62c3affb9d7e183ebe7a0c6ac743c0d8cc37961e4c2071f7925b6.json
├── query-3db2f4454d39b3f74800f3365cb706782494aae97669a83e07a84c7e7cb109de.json
├── query-543731a12e372132dea28e46a33e497d95ac37215411454d69c17ae2fdbb9c40.json
├── query-55028278133f6281dcc44c3bfee83503263a8e4a66122219fa193c46333419d6.json
├── query-5a58be59c5bc784eddb02472c0e0b84c27423b93f8adb038845a2a7ba1a7c70b.json
├── query-61b6ec20b59722891e39e3c9926e6caa9e6a14cc86de4100eaa0f74731a04918.json
├── query-847361b8b35d6daf7dcb40c7e313af26d196d794af755cadeac4e2c608e9b198.json
├── query-9b75008f29ec1a77219a34f2f5c2a74d28cb6d68688d49d5a85ff076177d4e4f.json
├── query-9bb4ddf808e9879ddd6cecebc3cd7517fc40081ae0b95a3b232aad26232c873e.json
├── query-b1ca112d29ed6b3158b07a6226c8e5ec000cd1f00b91691ecb69fda985ee2886.json
├── query-bb63248a41a6b0792290492869b55a5ebe54b5564380b462dac5e779fb61144a.json
├── query-c7a058a5fd638c6deb933f7b6d143119e5d15599be07c581f5333ab872202dcb.json
├── query-d42108e4bf27ca3d1d18c8a94a50e9bf87d5132028d0e03a131bfc8538272d9b.json
├── query-f6edc880836d9f6ee19dc817194c9de68b3c817309b181a0bfb708871726d8e4.json
└── query-fd7b310fbf4fc594d28a2980895ded5c6e3ac13543b73665d520e7b3a396a21d.json
├── .vscode
└── settings.json
├── Cargo.lock
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── Makefile.toml
├── README.md
├── chuckle-gateway
├── Cargo.toml
└── src
│ ├── events
│ ├── interaction_create.rs
│ ├── mod.rs
│ └── thread_create.rs
│ └── lib.rs
├── chuckle-github
├── Cargo.toml
├── data
│ └── pull_request_review_comment.json
└── src
│ ├── lib.rs
│ └── pull_request_review_comment.rs
├── chuckle-http
├── Cargo.toml
└── src
│ ├── lib.rs
│ ├── routes
│ ├── mod.rs
│ ├── status.rs
│ └── webhooks.rs
│ └── util
│ ├── error.rs
│ └── mod.rs
├── chuckle-interactions
├── Cargo.toml
├── commands.lock.json
├── deploy-commands.sh
└── src
│ ├── commands
│ ├── breakout_rooms.rs
│ ├── config
│ │ ├── breakout_category.rs
│ │ ├── default_org.rs
│ │ ├── default_repo.rs
│ │ ├── forum_log.rs
│ │ └── mod.rs
│ ├── hexil.rs
│ ├── link_github.rs
│ ├── mod.rs
│ ├── ping.rs
│ ├── pr_comments.rs
│ └── threads.rs
│ ├── context_menu
│ ├── circle_back.rs
│ └── mod.rs
│ ├── lib.rs
│ └── main.rs
├── chuckle-jobs
├── Cargo.toml
└── src
│ ├── circle_back.rs
│ ├── lib.rs
│ └── sweep_notifications.rs
├── chuckle-util
├── Cargo.toml
└── src
│ ├── chunkify.rs
│ ├── config.rs
│ ├── db.rs
│ ├── lib.rs
│ ├── state.rs
│ └── timestamptz.rs
├── chuckle
├── Cargo.toml
├── Dockerfile
└── src
│ └── main.rs
├── docker-compose.yml
├── migrations
├── 0_init.sql
├── 1_modal.sql
├── 2_pr_review.sql
├── 3_hexil.sql
├── 4_guild_settings.sql
├── 5_breakout_rooms.sql
├── 6_drop_modal.sql
└── README.md
└── rustfmt.toml
/.dockerignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | docker
3 | target
4 | .dockerignore
5 | .gitignore
6 | build.rs
7 | commands.lock.json
8 | deploy-commands.sh
9 | README.md
10 | y.*
11 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | DATABASE_URL="postgres://admin:oafishcaveman@localhost:5432/chuckle"
2 | #SQLX_OFFLINE=true
3 |
--------------------------------------------------------------------------------
/.envrc.example:
--------------------------------------------------------------------------------
1 | export DATABASE_URL="postgres://admin:oafishcaveman@localhost:5432/chuckle"
2 | export ENV=development
3 | export DISCORD_APPLICATION_ID=
4 | export DISCORD_TOKEN=
5 | export RUST_LOG=debug,hyper=info,tower_http=info,rustls=info
6 | export GITHUB_WEBHOOK_SECRET=
7 | # for getting repo files
8 | export GITHUB_ACCESS_TOKEN=
9 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | **Please describe the changes this PR makes and why it should be merged:**
2 | This PR:
3 | -
4 |
5 | **Status and versioning classification:**
6 |
7 |
12 |
--------------------------------------------------------------------------------
/.github/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
40 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "cargo": {
4 | "packageRules": [
5 | {
6 | "automerge": true,
7 | "matchCurrentVersion": "/^0\\./",
8 | "matchUpdateTypes": [
9 | "patch"
10 | ],
11 | "platformAutomerge": true
12 | },
13 | {
14 | "automerge": true,
15 | "matchCurrentVersion": ">=1.0.0",
16 | "matchUpdateTypes": [
17 | "minor",
18 | "patch"
19 | ],
20 | "platformAutomerge": true
21 | }
22 | ],
23 | "rangeStrategy": "bump"
24 | },
25 | "cloneSubmodules": true,
26 | "extends": [
27 | "config:base",
28 | "group:allNonMajor",
29 | ":dependencyDashboard"
30 | ],
31 | "labels": [
32 | "dependencies"
33 | ],
34 | "schedule": [
35 | "after 6pm"
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/.github/workflows/audit.yml:
--------------------------------------------------------------------------------
1 | name: Security audit
2 |
3 | on:
4 | schedule:
5 | - cron: "0 0 * * *"
6 | push:
7 | paths:
8 | - "**/Cargo.toml"
9 | - "**/Cargo.lock"
10 |
11 | jobs:
12 | audit:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: Checkout repository
16 | uses: actions/checkout@v4
17 | with:
18 | ref: ${{ github.event.pull_request.head.ref }}
19 | fetch-depth: 0
20 |
21 | - name: Checkout Repository
22 | uses: actions/checkout@v4
23 |
24 | - name: Register Problem Matchers
25 | uses: r7kamura/rust-problem-matchers@v1
26 |
27 | - run: rustup toolchain install stable --profile minimal
28 |
29 | - name: Cache Cargo
30 | uses: Swatinem/rust-cache@v2
31 |
32 | - name: Setup Rust
33 | uses: dtolnay/rust-toolchain@stable
34 |
35 | - name: Install Cargo Make
36 | uses: davidB/rust-cargo-make@v1
37 |
38 | - name: Run security audit
39 | run: cargo make audit
40 |
--------------------------------------------------------------------------------
/.github/workflows/cd.yml:
--------------------------------------------------------------------------------
1 | name: Publish Image
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | env:
9 | slug: ${{ secrets.DOCKER_SLUG }}
10 | dockerfile: ./chuckle/Dockerfile
11 |
12 | jobs:
13 | publish:
14 | runs-on: chortle
15 | steps:
16 | - name: Checkout Repository
17 | uses: actions/checkout@v4
18 |
19 | - name: Generate Image Tag
20 | id: generate_tag
21 | uses: trufflehq/truffle-packages/actions/image_tag@main
22 |
23 | - name: Set up Docker Buildx
24 | uses: docker/setup-buildx-action@v3
25 |
26 | - name: Cache Docker layers
27 | uses: actions/cache@v3
28 | with:
29 | path: /tmp/.buildx-cache
30 | key: ${{ runner.os }}-buildx-${{ github.sha }}
31 | restore-keys: |
32 | ${{ runner.os }}-buildx-
33 |
34 | - name: Login to Google Container Registry
35 | uses: docker/login-action@v3
36 | with:
37 | registry: gcr.io
38 | username: _json_key
39 | password: ${{ secrets.GCR_JSON_KEY }}
40 |
41 | - name: Docker meta
42 | id: meta
43 | uses: docker/metadata-action@v5
44 | with:
45 | images: ${{ env.slug }}
46 | tags: |
47 | type=raw,value=latest
48 | type=raw,value=${{ steps.generate_tag.outputs.tag }}
49 |
50 | - name: Build
51 | uses: docker/build-push-action@v5
52 | with:
53 | context: .
54 | file: ${{ env.dockerfile }}
55 | push: true
56 | tags: ${{ steps.meta.outputs.tags }}
57 | cache-from: type=local,src=/tmp/.buildx-cache
58 | cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
59 |
60 | # Temp fix
61 | # https://github.com/docker/build-push-action/issues/252
62 | # https://github.com/moby/buildkit/issues/1896
63 | - name: Move cache
64 | run: |
65 | rm -rf /tmp/.buildx-cache
66 | mv /tmp/.buildx-cache-new /tmp/.buildx-cache
67 |
--------------------------------------------------------------------------------
/.github/workflows/commands.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Updated Global Commands
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - 'chuckle-interactions/commands.lock.json'
9 | - '.github/workflows/commands.yml'
10 | workflow_dispatch:
11 |
12 | jobs:
13 | deploy:
14 | runs-on: chortle
15 |
16 | steps:
17 | - name: Checkout Commands Lockfile
18 | uses: actions/checkout@v4
19 | with:
20 | sparse-checkout: |
21 | chuckle-interactions/commands.lock.json
22 | sparse-checkout-cone-mode: false
23 |
24 | - name: PUT Global Commands
25 | run: |
26 | curl -X PUT https://discord.com/api/v10/applications/${{ secrets.DISCORD_APPLICATION_ID }}/commands \
27 | -H "Authorization: Bot ${{ secrets.DISCORD_TOKEN }}" \
28 | -H "content-type: application/json" \
29 | -d @./chuckle-interactions/commands.lock.json | jq
30 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | env:
4 | SQLX_OFFLINE: true
5 | CARGO_TERM_COLOR: always
6 |
7 | on:
8 | push:
9 | branches:
10 | - main
11 | paths-ignore:
12 | - "**.md"
13 | pull_request:
14 | paths-ignore:
15 | - "**.md"
16 |
17 | concurrency:
18 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
19 | cancel-in-progress: true
20 |
21 | jobs:
22 | check:
23 | name: Test Suite
24 | runs-on: chortle
25 | steps:
26 | - name: Checkout
27 | uses: actions/checkout@v4
28 |
29 | - name: Setup Problem Matchers
30 | uses: r7kamura/rust-problem-matchers@v1
31 |
32 | - run: rustup toolchain install stable --profile minimal
33 |
34 | - name: Rust Cache
35 | uses: Swatinem/rust-cache@v2
36 |
37 | - name: Setup Rust
38 | uses: dtolnay/rust-toolchain@stable
39 |
40 | - name: Install Cargo Make
41 | uses: davidB/rust-cargo-make@v1
42 |
43 | - name: Run Formatter
44 | run: cargo make format-ci
45 |
46 | - name: Run Clippy
47 | run: cargo make lint-ci
48 |
49 | - name: Build
50 | run: cargo check --all-features
51 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | .env*
3 | !.env
4 | !.envrc.example
5 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/pre-commit/pre-commit-hooks
3 | rev: f8d8c45220230434bd7440d85a7f64c67bcdb952
4 | hooks:
5 | - id: trailing-whitespace
6 | - id: end-of-file-fixer
7 | - id: check-toml
8 | - id: check-yaml
9 | - id: check-added-large-files
10 |
11 | - repo: local
12 | hooks:
13 | - id: format
14 | name: format
15 | description: Format files with cargo make fmt.
16 | entry: cargo make format
17 | language: system
18 | types: [rust]
19 | pass_filenames: false
20 | - id: lint
21 | name: lint
22 | description: Lint files with cargo make lint.
23 | entry: cargo make lint
24 | language: system
25 | types: [rust]
26 | pass_filenames: false
27 | - id: check
28 | name: check
29 | description: Check files with Cargo Check
30 | entry: cargo check
31 | language: system
32 | types: [rust]
33 | pass_filenames: false
34 |
--------------------------------------------------------------------------------
/.sqlx/query-07ca64b10025a77a597f7d003c7bf4fdd0b1a7a9b23f9246c6dab120a91f0498.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update guild_settings set default_repository_owner = $1 where guild_id = $2",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text",
9 | "Text"
10 | ]
11 | },
12 | "nullable": []
13 | },
14 | "hash": "07ca64b10025a77a597f7d003c7bf4fdd0b1a7a9b23f9246c6dab120a91f0498"
15 | }
16 |
--------------------------------------------------------------------------------
/.sqlx/query-0b5aa446aa706d7d3b4f4e485d589f2c116d529b46e74f3440201d08b6c56bf4.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "select * from pr_review_output where pr_number = $1 and repo_owner = $2 and repo = $3;",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | },
11 | {
12 | "ordinal": 1,
13 | "name": "pr_number",
14 | "type_info": "Int4"
15 | },
16 | {
17 | "ordinal": 2,
18 | "name": "repo_owner",
19 | "type_info": "Text"
20 | },
21 | {
22 | "ordinal": 3,
23 | "name": "repo",
24 | "type_info": "Text"
25 | },
26 | {
27 | "ordinal": 4,
28 | "name": "thread_id",
29 | "type_info": "Text"
30 | },
31 | {
32 | "ordinal": 5,
33 | "name": "created_at",
34 | "type_info": "Timestamptz"
35 | }
36 | ],
37 | "parameters": {
38 | "Left": [
39 | "Int4",
40 | "Text",
41 | "Text"
42 | ]
43 | },
44 | "nullable": [
45 | false,
46 | false,
47 | false,
48 | false,
49 | false,
50 | false
51 | ]
52 | },
53 | "hash": "0b5aa446aa706d7d3b4f4e485d589f2c116d529b46e74f3440201d08b6c56bf4"
54 | }
55 |
--------------------------------------------------------------------------------
/.sqlx/query-10fd2084c314c5a357f7408746702d90c908a44c3148b03de91bcf5eaee9712e.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update notifications set completed = true where id = $1",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Uuid"
9 | ]
10 | },
11 | "nullable": []
12 | },
13 | "hash": "10fd2084c314c5a357f7408746702d90c908a44c3148b03de91bcf5eaee9712e"
14 | }
15 |
--------------------------------------------------------------------------------
/.sqlx/query-14c35efbbe2e0c6c2edcec300f4f9f58257c5bf84a28f1ac2156539edc3ba6e0.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update \"user\" set github_id = $1 where id = $2 returning id",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | }
11 | ],
12 | "parameters": {
13 | "Left": [
14 | "Int4",
15 | "Uuid"
16 | ]
17 | },
18 | "nullable": [
19 | false
20 | ]
21 | },
22 | "hash": "14c35efbbe2e0c6c2edcec300f4f9f58257c5bf84a28f1ac2156539edc3ba6e0"
23 | }
24 |
--------------------------------------------------------------------------------
/.sqlx/query-1a7ad5f985960145009f82ab41eda810cf2f14b510e5196aaf6684d945daf017.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "insert into \"hexil\" (guild_id, user_id, role_id) values ($1, $2, $3)",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text",
9 | "Text",
10 | "Text"
11 | ]
12 | },
13 | "nullable": []
14 | },
15 | "hash": "1a7ad5f985960145009f82ab41eda810cf2f14b510e5196aaf6684d945daf017"
16 | }
17 |
--------------------------------------------------------------------------------
/.sqlx/query-2971ad0c94858b6c33d6ba863ea2e877fd574267d806ed1f7593c5cbb14ba1a2.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "insert into notifications (author_id, user_id, guild_id, channel_id, message_id, notify_at) values ($1, $2, $3, $4, $5, $6) returning id",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | }
11 | ],
12 | "parameters": {
13 | "Left": [
14 | "Int8",
15 | "Int8",
16 | "Int8",
17 | "Int8",
18 | "Int8",
19 | "Timestamptz"
20 | ]
21 | },
22 | "nullable": [
23 | false
24 | ]
25 | },
26 | "hash": "2971ad0c94858b6c33d6ba863ea2e877fd574267d806ed1f7593c5cbb14ba1a2"
27 | }
28 |
--------------------------------------------------------------------------------
/.sqlx/query-2a9879a85acc57d9a732657e57494dfb60c75f544f90e0c328e8c9bc34901f8e.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update guild_settings set breakout_rooms_category_id = $1 where guild_id = $2",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text",
9 | "Text"
10 | ]
11 | },
12 | "nullable": []
13 | },
14 | "hash": "2a9879a85acc57d9a732657e57494dfb60c75f544f90e0c328e8c9bc34901f8e"
15 | }
16 |
--------------------------------------------------------------------------------
/.sqlx/query-34c16baa9bad362981992f30483bf07b6af29662e693aad5f5ec31620b11c1a9.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update guild_settings set default_repository = null where guild_id = $1",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text"
9 | ]
10 | },
11 | "nullable": []
12 | },
13 | "hash": "34c16baa9bad362981992f30483bf07b6af29662e693aad5f5ec31620b11c1a9"
14 | }
15 |
--------------------------------------------------------------------------------
/.sqlx/query-3b3217f05ec62c3affb9d7e183ebe7a0c6ac743c0d8cc37961e4c2071f7925b6.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "insert into \"user\" (discord_id, github_id) values ($1, $2)",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text",
9 | "Int4"
10 | ]
11 | },
12 | "nullable": []
13 | },
14 | "hash": "3b3217f05ec62c3affb9d7e183ebe7a0c6ac743c0d8cc37961e4c2071f7925b6"
15 | }
16 |
--------------------------------------------------------------------------------
/.sqlx/query-3db2f4454d39b3f74800f3365cb706782494aae97669a83e07a84c7e7cb109de.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update guild_settings set default_repository_owner = null where guild_id = $1",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text"
9 | ]
10 | },
11 | "nullable": []
12 | },
13 | "hash": "3db2f4454d39b3f74800f3365cb706782494aae97669a83e07a84c7e7cb109de"
14 | }
15 |
--------------------------------------------------------------------------------
/.sqlx/query-543731a12e372132dea28e46a33e497d95ac37215411454d69c17ae2fdbb9c40.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "insert into guild_settings (guild_id) values ($1) returning *",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | },
11 | {
12 | "ordinal": 1,
13 | "name": "guild_id",
14 | "type_info": "Text"
15 | },
16 | {
17 | "ordinal": 2,
18 | "name": "forum_log_channel_id",
19 | "type_info": "Text"
20 | },
21 | {
22 | "ordinal": 3,
23 | "name": "default_repository",
24 | "type_info": "Text"
25 | },
26 | {
27 | "ordinal": 4,
28 | "name": "default_repository_owner",
29 | "type_info": "Text"
30 | },
31 | {
32 | "ordinal": 5,
33 | "name": "created_at",
34 | "type_info": "Timestamptz"
35 | },
36 | {
37 | "ordinal": 6,
38 | "name": "breakout_rooms_category_id",
39 | "type_info": "Text"
40 | }
41 | ],
42 | "parameters": {
43 | "Left": [
44 | "Text"
45 | ]
46 | },
47 | "nullable": [
48 | false,
49 | false,
50 | true,
51 | true,
52 | true,
53 | false,
54 | true
55 | ]
56 | },
57 | "hash": "543731a12e372132dea28e46a33e497d95ac37215411454d69c17ae2fdbb9c40"
58 | }
59 |
--------------------------------------------------------------------------------
/.sqlx/query-55028278133f6281dcc44c3bfee83503263a8e4a66122219fa193c46333419d6.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update guild_settings set forum_log_channel_id = $1 where guild_id = $2",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text",
9 | "Text"
10 | ]
11 | },
12 | "nullable": []
13 | },
14 | "hash": "55028278133f6281dcc44c3bfee83503263a8e4a66122219fa193c46333419d6"
15 | }
16 |
--------------------------------------------------------------------------------
/.sqlx/query-5a58be59c5bc784eddb02472c0e0b84c27423b93f8adb038845a2a7ba1a7c70b.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update guild_settings set breakout_rooms_category_id = null where guild_id = $1",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text"
9 | ]
10 | },
11 | "nullable": []
12 | },
13 | "hash": "5a58be59c5bc784eddb02472c0e0b84c27423b93f8adb038845a2a7ba1a7c70b"
14 | }
15 |
--------------------------------------------------------------------------------
/.sqlx/query-61b6ec20b59722891e39e3c9926e6caa9e6a14cc86de4100eaa0f74731a04918.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "select * from \"user\" where github_id = $1",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | },
11 | {
12 | "ordinal": 1,
13 | "name": "discord_id",
14 | "type_info": "Text"
15 | },
16 | {
17 | "ordinal": 2,
18 | "name": "github_id",
19 | "type_info": "Int4"
20 | },
21 | {
22 | "ordinal": 3,
23 | "name": "created_at",
24 | "type_info": "Timestamptz"
25 | }
26 | ],
27 | "parameters": {
28 | "Left": [
29 | "Int4"
30 | ]
31 | },
32 | "nullable": [
33 | false,
34 | true,
35 | true,
36 | false
37 | ]
38 | },
39 | "hash": "61b6ec20b59722891e39e3c9926e6caa9e6a14cc86de4100eaa0f74731a04918"
40 | }
41 |
--------------------------------------------------------------------------------
/.sqlx/query-847361b8b35d6daf7dcb40c7e313af26d196d794af755cadeac4e2c608e9b198.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "SELECT * FROM pr_review_output WHERE pr_number = $1 AND repo_owner = $2 AND repo = $3",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | },
11 | {
12 | "ordinal": 1,
13 | "name": "pr_number",
14 | "type_info": "Int4"
15 | },
16 | {
17 | "ordinal": 2,
18 | "name": "repo_owner",
19 | "type_info": "Text"
20 | },
21 | {
22 | "ordinal": 3,
23 | "name": "repo",
24 | "type_info": "Text"
25 | },
26 | {
27 | "ordinal": 4,
28 | "name": "thread_id",
29 | "type_info": "Text"
30 | },
31 | {
32 | "ordinal": 5,
33 | "name": "created_at",
34 | "type_info": "Timestamptz"
35 | }
36 | ],
37 | "parameters": {
38 | "Left": [
39 | "Int4",
40 | "Text",
41 | "Text"
42 | ]
43 | },
44 | "nullable": [
45 | false,
46 | false,
47 | false,
48 | false,
49 | false,
50 | false
51 | ]
52 | },
53 | "hash": "847361b8b35d6daf7dcb40c7e313af26d196d794af755cadeac4e2c608e9b198"
54 | }
55 |
--------------------------------------------------------------------------------
/.sqlx/query-9b75008f29ec1a77219a34f2f5c2a74d28cb6d68688d49d5a85ff076177d4e4f.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "SELECT * FROM \"user\" WHERE discord_id = $1",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | },
11 | {
12 | "ordinal": 1,
13 | "name": "discord_id",
14 | "type_info": "Text"
15 | },
16 | {
17 | "ordinal": 2,
18 | "name": "github_id",
19 | "type_info": "Int4"
20 | },
21 | {
22 | "ordinal": 3,
23 | "name": "created_at",
24 | "type_info": "Timestamptz"
25 | }
26 | ],
27 | "parameters": {
28 | "Left": [
29 | "Text"
30 | ]
31 | },
32 | "nullable": [
33 | false,
34 | true,
35 | true,
36 | false
37 | ]
38 | },
39 | "hash": "9b75008f29ec1a77219a34f2f5c2a74d28cb6d68688d49d5a85ff076177d4e4f"
40 | }
41 |
--------------------------------------------------------------------------------
/.sqlx/query-9bb4ddf808e9879ddd6cecebc3cd7517fc40081ae0b95a3b232aad26232c873e.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "insert into pr_review_output (pr_number, repo_owner, repo, thread_id) values ($1, $2, $3, $4) returning id",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | }
11 | ],
12 | "parameters": {
13 | "Left": [
14 | "Int4",
15 | "Text",
16 | "Text",
17 | "Text"
18 | ]
19 | },
20 | "nullable": [
21 | false
22 | ]
23 | },
24 | "hash": "9bb4ddf808e9879ddd6cecebc3cd7517fc40081ae0b95a3b232aad26232c873e"
25 | }
26 |
--------------------------------------------------------------------------------
/.sqlx/query-b1ca112d29ed6b3158b07a6226c8e5ec000cd1f00b91691ecb69fda985ee2886.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "select id, user_id, author_id, guild_id, channel_id, message_id, notify_at from notifications where notify_at < now() and completed = false",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | },
11 | {
12 | "ordinal": 1,
13 | "name": "user_id",
14 | "type_info": "Int8"
15 | },
16 | {
17 | "ordinal": 2,
18 | "name": "author_id",
19 | "type_info": "Int8"
20 | },
21 | {
22 | "ordinal": 3,
23 | "name": "guild_id",
24 | "type_info": "Int8"
25 | },
26 | {
27 | "ordinal": 4,
28 | "name": "channel_id",
29 | "type_info": "Int8"
30 | },
31 | {
32 | "ordinal": 5,
33 | "name": "message_id",
34 | "type_info": "Int8"
35 | },
36 | {
37 | "ordinal": 6,
38 | "name": "notify_at",
39 | "type_info": "Timestamptz"
40 | }
41 | ],
42 | "parameters": {
43 | "Left": []
44 | },
45 | "nullable": [
46 | false,
47 | false,
48 | false,
49 | false,
50 | false,
51 | false,
52 | false
53 | ]
54 | },
55 | "hash": "b1ca112d29ed6b3158b07a6226c8e5ec000cd1f00b91691ecb69fda985ee2886"
56 | }
57 |
--------------------------------------------------------------------------------
/.sqlx/query-bb63248a41a6b0792290492869b55a5ebe54b5564380b462dac5e779fb61144a.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update guild_settings set forum_log_channel_id = null where guild_id = $1",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text"
9 | ]
10 | },
11 | "nullable": []
12 | },
13 | "hash": "bb63248a41a6b0792290492869b55a5ebe54b5564380b462dac5e779fb61144a"
14 | }
15 |
--------------------------------------------------------------------------------
/.sqlx/query-c7a058a5fd638c6deb933f7b6d143119e5d15599be07c581f5333ab872202dcb.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update pr_review_output set thread_id = $1 where id = $2 returning id",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | }
11 | ],
12 | "parameters": {
13 | "Left": [
14 | "Text",
15 | "Uuid"
16 | ]
17 | },
18 | "nullable": [
19 | false
20 | ]
21 | },
22 | "hash": "c7a058a5fd638c6deb933f7b6d143119e5d15599be07c581f5333ab872202dcb"
23 | }
24 |
--------------------------------------------------------------------------------
/.sqlx/query-d42108e4bf27ca3d1d18c8a94a50e9bf87d5132028d0e03a131bfc8538272d9b.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "select * from guild_settings where guild_id = $1",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | },
11 | {
12 | "ordinal": 1,
13 | "name": "guild_id",
14 | "type_info": "Text"
15 | },
16 | {
17 | "ordinal": 2,
18 | "name": "forum_log_channel_id",
19 | "type_info": "Text"
20 | },
21 | {
22 | "ordinal": 3,
23 | "name": "default_repository",
24 | "type_info": "Text"
25 | },
26 | {
27 | "ordinal": 4,
28 | "name": "default_repository_owner",
29 | "type_info": "Text"
30 | },
31 | {
32 | "ordinal": 5,
33 | "name": "created_at",
34 | "type_info": "Timestamptz"
35 | },
36 | {
37 | "ordinal": 6,
38 | "name": "breakout_rooms_category_id",
39 | "type_info": "Text"
40 | }
41 | ],
42 | "parameters": {
43 | "Left": [
44 | "Text"
45 | ]
46 | },
47 | "nullable": [
48 | false,
49 | false,
50 | true,
51 | true,
52 | true,
53 | false,
54 | true
55 | ]
56 | },
57 | "hash": "d42108e4bf27ca3d1d18c8a94a50e9bf87d5132028d0e03a131bfc8538272d9b"
58 | }
59 |
--------------------------------------------------------------------------------
/.sqlx/query-f6edc880836d9f6ee19dc817194c9de68b3c817309b181a0bfb708871726d8e4.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "update guild_settings set default_repository = $1 where guild_id = $2",
4 | "describe": {
5 | "columns": [],
6 | "parameters": {
7 | "Left": [
8 | "Text",
9 | "Text"
10 | ]
11 | },
12 | "nullable": []
13 | },
14 | "hash": "f6edc880836d9f6ee19dc817194c9de68b3c817309b181a0bfb708871726d8e4"
15 | }
16 |
--------------------------------------------------------------------------------
/.sqlx/query-fd7b310fbf4fc594d28a2980895ded5c6e3ac13543b73665d520e7b3a396a21d.json:
--------------------------------------------------------------------------------
1 | {
2 | "db_name": "PostgreSQL",
3 | "query": "select * from hexil where guild_id = $1 and user_id = $2",
4 | "describe": {
5 | "columns": [
6 | {
7 | "ordinal": 0,
8 | "name": "id",
9 | "type_info": "Uuid"
10 | },
11 | {
12 | "ordinal": 1,
13 | "name": "guild_id",
14 | "type_info": "Text"
15 | },
16 | {
17 | "ordinal": 2,
18 | "name": "user_id",
19 | "type_info": "Text"
20 | },
21 | {
22 | "ordinal": 3,
23 | "name": "role_id",
24 | "type_info": "Text"
25 | },
26 | {
27 | "ordinal": 4,
28 | "name": "created_at",
29 | "type_info": "Timestamptz"
30 | }
31 | ],
32 | "parameters": {
33 | "Left": [
34 | "Text",
35 | "Text"
36 | ]
37 | },
38 | "nullable": [
39 | false,
40 | false,
41 | false,
42 | false,
43 | false
44 | ]
45 | },
46 | "hash": "fd7b310fbf4fc594d28a2980895ded5c6e3ac13543b73665d520e7b3a396a21d"
47 | }
48 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.inlayHints.enabled": "off"
3 | }
4 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = [
3 | "chuckle",
4 | "chuckle-gateway",
5 | "chuckle-github",
6 | "chuckle-http",
7 | "chuckle-interactions",
8 | "chuckle-jobs",
9 | "chuckle-util"
10 | ]
11 | resolver = "2"
12 |
13 | [workspace.package]
14 | version = "0.1.0"
15 | license = "MIT OR Apache-2.0"
16 | edition = "2021"
17 | repository = "https://github.com/trufflehq/chuckle"
18 | keywords = ["discord", "trufflehq", "truffle", "youtube", "twitch"]
19 | authors = [
20 | "Carter Himmel "
21 | ]
22 | homepage = "https://github.com/trufflehq/chuckle#readme"
23 |
24 | [workspace.dependencies]
25 | chuckle-gateway = { path = "./chuckle-gateway" }
26 | chuckle-github = { path = "./chuckle-github" }
27 | chuckle-http = { path = "./chuckle-http" }
28 | chuckle-interactions = { path = "./chuckle-interactions", default-features = false }
29 | chuckle-jobs = { path = "./chuckle-jobs" }
30 | chuckle-util = { path = "./chuckle-util" }
31 |
32 | anyhow = "1"
33 | async-trait = "0.1"
34 | axum = { version = "0.6", features = ["macros", "multipart"] }
35 | chrono = "0.4"
36 | hex = "0.4"
37 | once_cell = "1"
38 | redis = { version = "0.23", features = ["tokio-comp", "connection-manager"] }
39 | reqwest = { version = "0.11", features = ["json"] }
40 | serde = { version = "1", features = ["derive"] }
41 | serde_json = { version = "1", features = ["preserve_order"] }
42 | tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }
43 | sqlx = { version = "0.7", features = [ "runtime-tokio-rustls", "postgres", "uuid", "time" ] }
44 | time = "0.3"
45 | tracing = "0.1"
46 | tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter", "json"] }
47 | twilight-cache-inmemory = { version = "0.15", features = ["permission-calculator"] }
48 | twilight-gateway = { version = "0.15", default-features = false, features = ["rustls-webpki-roots"] }
49 | twilight-http = { version = "0.15", default-features = false, features = ["rustls-webpki-roots"] }
50 | twilight-model = "0.15"
51 | twilight-util = { version = "0.15", features = ["builder"] }
52 | uuid = { version = "1", features = ["serde", "v4"] }
53 | vesper = { version = "0.11", features = ["bulk"] }
54 |
55 | [profile.dev.package.sqlx-macros]
56 | opt-level = 3
57 |
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2023 Spore, Inc.
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Spore, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile.toml:
--------------------------------------------------------------------------------
1 | [env]
2 | CARGO_MAKE_WORKSPACE_EMULATION = true
3 |
4 | [config]
5 | main_project_member = "chuckle"
6 | default_to_workspace = false
7 |
8 | [tasks.setup]
9 | script = '''
10 | echo # installing git hooks
11 | pre-commit --version || pip install pre-commit
12 | pre-commit install || echo "failed to install git hooks!" 1>&2
13 |
14 | echo # things required by sqlx
15 | cargo install sqlx-cli@0.7.2 --no-default-features --features native-tls,postgres
16 |
17 | echo # things required by `cargo make sort-deps`
18 | cargo install cargo-sort
19 | '''
20 |
21 | [tasks.lint]
22 | command = "cargo"
23 | args = [
24 | "clippy",
25 | "--tests",
26 | "--examples",
27 | "--all-targets",
28 | "--all-features",
29 | "--workspace",
30 | ]
31 | env = { SQLX_OFFLINE = "true" }
32 |
33 | [tasks.lint-ci]
34 | command = "cargo"
35 | args = [
36 | "clippy",
37 | "--tests",
38 | "--examples",
39 | "--all-targets",
40 | "--all-features",
41 | "--workspace",
42 | "--",
43 | "-D",
44 | "warnings",
45 | ]
46 |
47 | [tasks.sort-deps]
48 | command = "cargo"
49 | args = [
50 | "sort",
51 | "--workspace",
52 | "--grouped"
53 | ]
54 |
55 | [tasks.format]
56 | install_crate = "rustfmt"
57 | command = "cargo"
58 | args = ["fmt", "--all"]
59 |
60 | [tasks.fmt]
61 | alias = "format"
62 |
63 | [tasks.format-ci]
64 | install_crate = "rustfmt"
65 | command = "cargo"
66 | args = ["fmt", "--all", "--", "--check"]
67 |
68 | [tasks.audit]
69 | command = "cargo"
70 | args = ["audit"]
71 |
72 | [tasks.timings]
73 | script = '''
74 | cargo clean
75 | cargo build --release --quiet --timings
76 | xdg-open /target/cargo-timings/cargo-timing.html
77 | '''
78 |
79 | [tasks.dev]
80 | env = { RUST_LOG = "info" }
81 | command = "cargo"
82 | args = ["run", "--bin", "chuckle"]
83 | watch = { watch = ["chuckle", "chuckle-gateway", "chuckle-interactions", "chuckle-util"] }
84 |
85 | [tasks.commands-lockfile]
86 | command = "cargo"
87 | args = ["run", "--bin", "chuckle-interactions"]
88 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |

8 |

9 |

10 |
11 |
12 | ## About
13 | Chuckle is our in-house Discord bot for our internal company server.
14 | We weren't a huge fan of Slack and, most of our target demographic uses Discord.
15 |
16 | A few of our favorite (and only :p) features include:
17 | - Circle Back, create reminders to revisit a specific message
18 | - PR Comments, stream PR reviews and updates to a configured thread
19 | - `/hexil`, allow each member to set a custom role/name color
20 |
21 | # Development
22 |
23 | ## Requirements
24 | These are some broad, general requirements for running Chuckle.
25 | - [Rust](https://rust-lang.org/tools/install)
26 | - [Docker](https://docs.docker.com/engine/install/)
27 |
28 | ## Setup
29 | Before you can actually setup... we have some setup to do!
30 |
31 | 1. Install [`cargo-make`](https://github.com/sagiegurari/cargo-make#installation) (`cargo install --force cargo-make`)
32 |
33 | Cargo doesn't have a native "scripts" feature like Yarn or NPM. Thus, we use `cargo-make` and [`Makefile.toml`](./Makefile.toml).
34 |
35 | ~~2. Install [`pre-commit`](https://pre-commit.com/#installation) (`pip install pre-commit`)~~
36 |
37 | ~~We use this for running git hooks.~~ This is handled by the next step.
38 |
39 | 2. Run `cargo make setup`
40 |
41 | This installs necessary components for other scripts and development fun.
42 |
43 | ### Environment
44 | If it weren't for `sqlx` and it's inability to play nice with `direnv`, we wouldn't also need an `.env` file containing just the `DATABASE_URL`.
45 |
46 | 1. Install [direnv](https://direnv.net/#basic-installation).
47 |
48 | It automatically loads our `.direnv` file.
49 |
50 | 2. Copy `.envrc.example` to `.envrc` and fill with your environment variables.
51 |
52 | 3. Ensure `.env` houses your `DATABASE_URL` address.
53 |
54 | ### Database
55 | We utilize `sqlx`'s compile-time checked queries, which requires a database connection during development.
56 | Additionally, we use `sqlx`'s migrations tool, which is just a treat!
57 |
58 | 1. Start the database with `docker compose up -d`.
59 |
60 | 2. Run `sqlx migrate run`
61 |
62 | This applies our database migrations.
63 |
64 | ## Running
65 | Now, running the bot should be as easy as:
66 |
67 | 1. `cargo make dev`
68 |
69 | ## Contributing
70 | When making changes to Chuckle, there are a few things you must take into consideration.
71 |
72 | If you make any query changes, you must run `cargo sqlx prepare` to create an entry in [`.sqlx`](./.sqlx) to support `SQLX_OFFLINE`.
73 | If you make any command/interaction data changes, you must run `cargo make commands-lockfile` to remake the [commands.lock.json](./chuckle-interactions/commands.lock.json) file.
74 |
75 | Regardless, of what scope, you must always ensure Clippy, Rustfmt and cargo-check are satisified, as done with pre-commit hooks.
76 |
77 |
78 | # Production
79 | We currently host Chuckle on our Google Kubernetes Engine cluster.
80 | ```mermaid
81 | flowchart TD
82 | commands["
83 | Update Discord
84 | Commands
85 | "]
86 | test["
87 | Lint, Format
88 | and Build
89 | "]
90 | commit[Push to main] --> test
91 | commit ---> deploy[Deploy to GCR]
92 | commit -- "
93 | commands.lock.json
94 | updated?
95 | " ---> commands
96 | commit -- "
97 | migrations
98 | updated?
99 | " --> cloudsql[Connect to Cloud SQL]
100 | --> migrations[Apply Migrations]
101 | ```
102 |
103 | ## Building Chuckle
104 |
105 | todo, see our [.github/workflows](./.github/workflows)
106 |
--------------------------------------------------------------------------------
/chuckle-gateway/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "chuckle-gateway"
3 | description = "The Discord gateway connection for Chuckle."
4 | version = { workspace = true }
5 | license = { workspace = true }
6 | edition = { workspace = true }
7 | authors = { workspace = true }
8 | repository = { workspace = true }
9 | homepage = { workspace = true }
10 |
11 | [dependencies]
12 | chuckle-interactions = { workspace = true }
13 | chuckle-util = { workspace = true }
14 |
15 | anyhow = { workspace = true }
16 | once_cell = { workspace = true }
17 | tokio = { workspace = true, features = ["rt"] }
18 | tracing = { workspace = true }
19 | twilight-gateway = { workspace = true }
20 | twilight-model = { workspace = true }
21 |
--------------------------------------------------------------------------------
/chuckle-gateway/src/events/interaction_create.rs:
--------------------------------------------------------------------------------
1 | use chuckle_interactions::ChuckleFramework;
2 | use twilight_model::{
3 | application::interaction::{InteractionData, InteractionType},
4 | gateway::payload::incoming::InteractionCreate,
5 | };
6 |
7 | pub async fn handle(
8 | framework: ChuckleFramework,
9 | event: Box,
10 | ) -> anyhow::Result<()> {
11 | if !event.is_guild() {
12 | return Ok(()); // dms
13 | }
14 | let interaction = event.0;
15 | tracing::info!("Received an interaction {:#?}", interaction.kind);
16 |
17 | match interaction.kind {
18 | InteractionType::Ping => unimplemented!("should be unnecessary via gateway"),
19 | InteractionType::ApplicationCommand => match interaction.clone().data {
20 | Some(InteractionData::ApplicationCommand(data)) => {
21 | tracing::info!("received application command: {:?}", data.kind);
22 | framework.process(interaction).await;
23 |
24 | Ok(())
25 | }
26 | _ => Ok(()),
27 | },
28 | InteractionType::ModalSubmit => {
29 | framework.process(interaction).await;
30 |
31 | Ok(())
32 | }
33 | InteractionType::MessageComponent => unimplemented!(),
34 | InteractionType::ApplicationCommandAutocomplete => unimplemented!(""),
35 | _ => unimplemented!(),
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/chuckle-gateway/src/events/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod interaction_create;
2 | pub mod thread_create;
3 |
--------------------------------------------------------------------------------
/chuckle-gateway/src/events/thread_create.rs:
--------------------------------------------------------------------------------
1 | use chuckle_util::{db::get_settings, ChuckleState};
2 | use std::str::FromStr;
3 | use twilight_model::{
4 | channel::ChannelType,
5 | gateway::payload::incoming::ThreadCreate,
6 | id::{marker::ChannelMarker, Id},
7 | };
8 |
9 | pub async fn handle(state: ChuckleState, event: Box) -> anyhow::Result<()> {
10 | if event.parent_id.is_none() && event.guild_id.is_none() {
11 | return Ok(()); // not a thread
12 | }
13 |
14 | if !event.newly_created.unwrap_or(false) {
15 | return Ok(()); // not a new thread
16 | }
17 |
18 | let parent = state.http_client.channel(event.parent_id.unwrap()).await;
19 | let parent = match parent {
20 | Ok(parent) => parent.model().await?,
21 | Err(_) => return Ok(()), // parent channel not found
22 | };
23 |
24 | if parent.kind != ChannelType::GuildForum {
25 | return Ok(()); // non-forum
26 | }
27 |
28 | let settings = get_settings(&state, event.guild_id.unwrap()).await?;
29 | if settings.forum_log_channel_id.is_none() {
30 | return Ok(()); // no forum log channel set
31 | }
32 | let log_id: Id =
33 | Id::::from_str(&settings.forum_log_channel_id.unwrap()).unwrap();
34 |
35 | let content = format!(
36 | "<@{}> created <#{}> in <#{}>",
37 | event.owner_id.unwrap(),
38 | event.id,
39 | parent.id
40 | );
41 | let _ = state
42 | .http_client
43 | .create_message(log_id)
44 | .content(&content)?
45 | .await;
46 |
47 | Ok(())
48 | }
49 |
--------------------------------------------------------------------------------
/chuckle-gateway/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod events;
2 |
3 | use crate::events::{interaction_create, thread_create};
4 | use anyhow::Result;
5 | use chuckle_interactions::ChuckleFramework;
6 | use chuckle_util::{ChuckleState, CONFIG};
7 | use twilight_gateway::{Config, Event, EventTypeFlags, Intents, Shard, ShardId};
8 |
9 | const BOT_EVENTS: EventTypeFlags = EventTypeFlags::from_bits_truncate(
10 | EventTypeFlags::READY.bits()
11 | | EventTypeFlags::GUILD_CREATE.bits()
12 | | EventTypeFlags::GUILD_DELETE.bits()
13 | | EventTypeFlags::THREAD_CREATE.bits()
14 | | EventTypeFlags::CHANNEL_CREATE.bits()
15 | | EventTypeFlags::CHANNEL_UPDATE.bits()
16 | | EventTypeFlags::CHANNEL_DELETE.bits()
17 | | EventTypeFlags::INTERACTION_CREATE.bits()
18 | | EventTypeFlags::GUILD_VOICE_STATES.bits()
19 | | EventTypeFlags::GUILD_MEMBERS.bits(),
20 | );
21 |
22 | pub async fn create_gateway(state: ChuckleState, framework: ChuckleFramework) -> Result<()> {
23 | let config = Config::builder(
24 | CONFIG.discord_token.clone(),
25 | Intents::GUILDS | Intents::GUILD_VOICE_STATES | Intents::GUILD_MEMBERS,
26 | )
27 | .event_types(BOT_EVENTS)
28 | .build();
29 | let mut shard = Shard::with_config(ShardId::ONE, config);
30 |
31 | loop {
32 | let event = match shard.next_event().await {
33 | Ok(event) => event,
34 | Err(source) => {
35 | tracing::warn!(?source, "error recieving event");
36 | continue;
37 | }
38 | };
39 |
40 | let state = state.clone();
41 | let framework = framework.clone();
42 | tokio::spawn(handle_event(state, framework, event));
43 | }
44 | }
45 |
46 | #[allow(clippy::unit_arg)]
47 | pub async fn handle_event(
48 | state: ChuckleState,
49 | framework: ChuckleFramework,
50 | event: Event,
51 | ) -> Result<()> {
52 | let shard_id = 1;
53 | state.cache.update(&event);
54 |
55 | match event {
56 | Event::GatewayHeartbeat(heart) => {
57 | Ok(tracing::debug!("Shard {shard_id} heartbeat: {heart}"))
58 | }
59 | Event::ThreadCreate(event) => thread_create::handle(state, event).await,
60 | Event::InteractionCreate(event) => interaction_create::handle(framework, event).await,
61 | Event::Ready(_) => Ok(tracing::info!("Shard {shard_id} connected; client ready!")),
62 | Event::GatewayReconnect => Ok(tracing::info! {
63 | target: "gateway_reconnect",
64 | "shard {shard_id} gateway reconnecting"
65 | }),
66 | Event::GuildCreate(guild) => Ok(tracing::info!(
67 | "guild_create: received {} ({})",
68 | guild.name,
69 | guild.id,
70 | )),
71 | Event::GuildDelete(guild) => Ok(tracing::info!(
72 | "[event::guilddelete] shard {shard_id} guild delete {}",
73 | guild.id
74 | )),
75 | _ => Ok(tracing::debug!(
76 | "shard {shard_id} emitted {:?}",
77 | event.kind()
78 | )),
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/chuckle-github/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "chuckle-github"
3 | description = "GitHub models and logic for PR review comments &e"
4 | version = { workspace = true }
5 | license = { workspace = true }
6 | edition = { workspace = true }
7 | authors = { workspace = true }
8 | repository = { workspace = true }
9 | homepage = { workspace = true }
10 |
11 | [dependencies]
12 | anyhow = { workspace = true }
13 | reqwest = { workspace = true }
14 | serde = { workspace = true }
15 | serde_json = { workspace = true }
16 |
17 | [dev-dependencies]
18 | tokio = { workspace = true }
19 | format_serde_error = "0.3"
20 |
--------------------------------------------------------------------------------
/chuckle-github/data/pull_request_review_comment.json:
--------------------------------------------------------------------------------
1 | {
2 | "action": "created",
3 | "comment": {
4 | "_links": {
5 | "html": {
6 | "href": "https://github.com/trufflehq/chonk/pull/116#discussion_r1251639456"
7 | },
8 | "pull_request": {
9 | "href": "https://api.github.com/repos/trufflehq/chonk/pulls/116"
10 | },
11 | "self": {
12 | "href": "https://api.github.com/repos/trufflehq/chonk/pulls/comments/1251639456"
13 | }
14 | },
15 | "author_association": "CONTRIBUTOR",
16 | "body": "don't forget to always use `> `",
17 | "commit_id": "c71e4dfab43e7ede9919e5adb51a717366c798cc",
18 | "created_at": "2023-07-04T07:52:39Z",
19 | "diff_hunk": "@@ -0,0 +1,78 @@\n+import { css } from '../../../deps/styles.ts';\n+\n+export default css`\n+ .p-global-profile-page {\n+ margin-left: 40px;\n+ max-width: 680px;\n+ margin-top: 32px;\n+ > .c-edit-me-form {\n+ max-width: 440px;\n+ margin: 12px 0px;\n+ margin-bottom: 30px;\n+ h3 {",
20 | "html_url": "https://github.com/trufflehq/chonk/pull/116#discussion_r1251639456",
21 | "id": 1251639456,
22 | "line": 12,
23 | "node_id": "PRRC_kwDOIWNBLM5KmoCg",
24 | "original_commit_id": "c71e4dfab43e7ede9919e5adb51a717366c798cc",
25 | "original_line": 12,
26 | "original_position": 12,
27 | "original_start_line": null,
28 | "path": "sites/app.truffle.vip/src/pages/user-settings/global-profile-page/global-profile-page.scss.ts",
29 | "position": 12,
30 | "pull_request_review_id": 1512289374,
31 | "pull_request_url": "https://api.github.com/repos/trufflehq/chonk/pulls/116",
32 | "reactions": {
33 | "+1": 0,
34 | "-1": 0,
35 | "confused": 0,
36 | "eyes": 0,
37 | "heart": 0,
38 | "hooray": 0,
39 | "laugh": 0,
40 | "rocket": 0,
41 | "total_count": 0,
42 | "url": "https://api.github.com/repos/trufflehq/chonk/pulls/comments/1251639456/reactions"
43 | },
44 | "side": "RIGHT",
45 | "start_line": null,
46 | "start_side": null,
47 | "subject_type": "line",
48 | "updated_at": "2023-07-04T07:52:40Z",
49 | "url": "https://api.github.com/repos/trufflehq/chonk/pulls/comments/1251639456",
50 | "user": {
51 | "avatar_url": "https://avatars.githubusercontent.com/u/1271767?v=4",
52 | "events_url": "https://api.github.com/users/austinhallock/events{/privacy}",
53 | "followers_url": "https://api.github.com/users/austinhallock/followers",
54 | "following_url": "https://api.github.com/users/austinhallock/following{/other_user}",
55 | "gists_url": "https://api.github.com/users/austinhallock/gists{/gist_id}",
56 | "gravatar_id": "",
57 | "html_url": "https://github.com/austinhallock",
58 | "id": 1271767,
59 | "login": "austinhallock",
60 | "node_id": "MDQ6VXNlcjEyNzE3Njc=",
61 | "organizations_url": "https://api.github.com/users/austinhallock/orgs",
62 | "received_events_url": "https://api.github.com/users/austinhallock/received_events",
63 | "repos_url": "https://api.github.com/users/austinhallock/repos",
64 | "site_admin": false,
65 | "starred_url": "https://api.github.com/users/austinhallock/starred{/owner}{/repo}",
66 | "subscriptions_url": "https://api.github.com/users/austinhallock/subscriptions",
67 | "type": "User",
68 | "url": "https://api.github.com/users/austinhallock"
69 | }
70 | },
71 | "organization": {
72 | "avatar_url": "https://avatars.githubusercontent.com/u/76624237?v=4",
73 | "description": "Building cool shit for creators.",
74 | "events_url": "https://api.github.com/orgs/trufflehq/events",
75 | "hooks_url": "https://api.github.com/orgs/trufflehq/hooks",
76 | "id": 76624237,
77 | "issues_url": "https://api.github.com/orgs/trufflehq/issues",
78 | "login": "trufflehq",
79 | "members_url": "https://api.github.com/orgs/trufflehq/members{/member}",
80 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2NjI0MjM3",
81 | "public_members_url": "https://api.github.com/orgs/trufflehq/public_members{/member}",
82 | "repos_url": "https://api.github.com/orgs/trufflehq/repos",
83 | "url": "https://api.github.com/orgs/trufflehq"
84 | },
85 | "pull_request": {
86 | "_links": {
87 | "comments": {
88 | "href": "https://api.github.com/repos/trufflehq/chonk/issues/116/comments"
89 | },
90 | "commits": {
91 | "href": "https://api.github.com/repos/trufflehq/chonk/pulls/116/commits"
92 | },
93 | "html": {
94 | "href": "https://github.com/trufflehq/chonk/pull/116"
95 | },
96 | "issue": {
97 | "href": "https://api.github.com/repos/trufflehq/chonk/issues/116"
98 | },
99 | "review_comment": {
100 | "href": "https://api.github.com/repos/trufflehq/chonk/pulls/comments{/number}"
101 | },
102 | "review_comments": {
103 | "href": "https://api.github.com/repos/trufflehq/chonk/pulls/116/comments"
104 | },
105 | "self": {
106 | "href": "https://api.github.com/repos/trufflehq/chonk/pulls/116"
107 | },
108 | "statuses": {
109 | "href": "https://api.github.com/repos/trufflehq/chonk/statuses/c71e4dfab43e7ede9919e5adb51a717366c798cc"
110 | }
111 | },
112 | "active_lock_reason": null,
113 | "assignee": null,
114 | "assignees": [],
115 | "author_association": "CONTRIBUTOR",
116 | "auto_merge": null,
117 | "base": {
118 | "label": "trufflehq:main",
119 | "ref": "main",
120 | "repo": {
121 | "allow_auto_merge": false,
122 | "allow_forking": false,
123 | "allow_merge_commit": false,
124 | "allow_rebase_merge": false,
125 | "allow_squash_merge": true,
126 | "allow_update_branch": false,
127 | "archive_url": "https://api.github.com/repos/trufflehq/chonk/{archive_format}{/ref}",
128 | "archived": false,
129 | "assignees_url": "https://api.github.com/repos/trufflehq/chonk/assignees{/user}",
130 | "blobs_url": "https://api.github.com/repos/trufflehq/chonk/git/blobs{/sha}",
131 | "branches_url": "https://api.github.com/repos/trufflehq/chonk/branches{/branch}",
132 | "clone_url": "https://github.com/trufflehq/chonk.git",
133 | "collaborators_url": "https://api.github.com/repos/trufflehq/chonk/collaborators{/collaborator}",
134 | "comments_url": "https://api.github.com/repos/trufflehq/chonk/comments{/number}",
135 | "commits_url": "https://api.github.com/repos/trufflehq/chonk/commits{/sha}",
136 | "compare_url": "https://api.github.com/repos/trufflehq/chonk/compare/{base}...{head}",
137 | "contents_url": "https://api.github.com/repos/trufflehq/chonk/contents/{+path}",
138 | "contributors_url": "https://api.github.com/repos/trufflehq/chonk/contributors",
139 | "created_at": "2022-10-31T21:07:22Z",
140 | "default_branch": "main",
141 | "delete_branch_on_merge": false,
142 | "deployments_url": "https://api.github.com/repos/trufflehq/chonk/deployments",
143 | "description": "all the services! so many codes! so much chonk!",
144 | "disabled": false,
145 | "downloads_url": "https://api.github.com/repos/trufflehq/chonk/downloads",
146 | "events_url": "https://api.github.com/repos/trufflehq/chonk/events",
147 | "fork": false,
148 | "forks": 0,
149 | "forks_count": 0,
150 | "forks_url": "https://api.github.com/repos/trufflehq/chonk/forks",
151 | "full_name": "trufflehq/chonk",
152 | "git_commits_url": "https://api.github.com/repos/trufflehq/chonk/git/commits{/sha}",
153 | "git_refs_url": "https://api.github.com/repos/trufflehq/chonk/git/refs{/sha}",
154 | "git_tags_url": "https://api.github.com/repos/trufflehq/chonk/git/tags{/sha}",
155 | "git_url": "git://github.com/trufflehq/chonk.git",
156 | "has_discussions": false,
157 | "has_downloads": true,
158 | "has_issues": true,
159 | "has_pages": false,
160 | "has_projects": false,
161 | "has_wiki": false,
162 | "homepage": "",
163 | "hooks_url": "https://api.github.com/repos/trufflehq/chonk/hooks",
164 | "html_url": "https://github.com/trufflehq/chonk",
165 | "id": 560152876,
166 | "is_template": false,
167 | "issue_comment_url": "https://api.github.com/repos/trufflehq/chonk/issues/comments{/number}",
168 | "issue_events_url": "https://api.github.com/repos/trufflehq/chonk/issues/events{/number}",
169 | "issues_url": "https://api.github.com/repos/trufflehq/chonk/issues{/number}",
170 | "keys_url": "https://api.github.com/repos/trufflehq/chonk/keys{/key_id}",
171 | "labels_url": "https://api.github.com/repos/trufflehq/chonk/labels{/name}",
172 | "language": "JavaScript",
173 | "languages_url": "https://api.github.com/repos/trufflehq/chonk/languages",
174 | "license": null,
175 | "merge_commit_message": "PR_TITLE",
176 | "merge_commit_title": "MERGE_MESSAGE",
177 | "merges_url": "https://api.github.com/repos/trufflehq/chonk/merges",
178 | "milestones_url": "https://api.github.com/repos/trufflehq/chonk/milestones{/number}",
179 | "mirror_url": null,
180 | "name": "chonk",
181 | "node_id": "R_kgDOIWNBLA",
182 | "notifications_url": "https://api.github.com/repos/trufflehq/chonk/notifications{?since,all,participating}",
183 | "open_issues": 5,
184 | "open_issues_count": 5,
185 | "owner": {
186 | "avatar_url": "https://avatars.githubusercontent.com/u/76624237?v=4",
187 | "events_url": "https://api.github.com/users/trufflehq/events{/privacy}",
188 | "followers_url": "https://api.github.com/users/trufflehq/followers",
189 | "following_url": "https://api.github.com/users/trufflehq/following{/other_user}",
190 | "gists_url": "https://api.github.com/users/trufflehq/gists{/gist_id}",
191 | "gravatar_id": "",
192 | "html_url": "https://github.com/trufflehq",
193 | "id": 76624237,
194 | "login": "trufflehq",
195 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2NjI0MjM3",
196 | "organizations_url": "https://api.github.com/users/trufflehq/orgs",
197 | "received_events_url": "https://api.github.com/users/trufflehq/received_events",
198 | "repos_url": "https://api.github.com/users/trufflehq/repos",
199 | "site_admin": false,
200 | "starred_url": "https://api.github.com/users/trufflehq/starred{/owner}{/repo}",
201 | "subscriptions_url": "https://api.github.com/users/trufflehq/subscriptions",
202 | "type": "Organization",
203 | "url": "https://api.github.com/users/trufflehq"
204 | },
205 | "private": true,
206 | "pulls_url": "https://api.github.com/repos/trufflehq/chonk/pulls{/number}",
207 | "pushed_at": "2023-07-04T01:46:28Z",
208 | "releases_url": "https://api.github.com/repos/trufflehq/chonk/releases{/id}",
209 | "size": 12722,
210 | "squash_merge_commit_message": "COMMIT_MESSAGES",
211 | "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
212 | "ssh_url": "git@github.com:trufflehq/chonk.git",
213 | "stargazers_count": 2,
214 | "stargazers_url": "https://api.github.com/repos/trufflehq/chonk/stargazers",
215 | "statuses_url": "https://api.github.com/repos/trufflehq/chonk/statuses/{sha}",
216 | "subscribers_url": "https://api.github.com/repos/trufflehq/chonk/subscribers",
217 | "subscription_url": "https://api.github.com/repos/trufflehq/chonk/subscription",
218 | "svn_url": "https://github.com/trufflehq/chonk",
219 | "tags_url": "https://api.github.com/repos/trufflehq/chonk/tags",
220 | "teams_url": "https://api.github.com/repos/trufflehq/chonk/teams",
221 | "topics": [],
222 | "trees_url": "https://api.github.com/repos/trufflehq/chonk/git/trees{/sha}",
223 | "updated_at": "2023-06-09T21:26:41Z",
224 | "url": "https://api.github.com/repos/trufflehq/chonk",
225 | "use_squash_pr_title_as_default": false,
226 | "visibility": "private",
227 | "watchers": 2,
228 | "watchers_count": 2,
229 | "web_commit_signoff_required": false
230 | },
231 | "sha": "34fa98ebb62e9d704a09c32bc990c7958ce07a32",
232 | "user": {
233 | "avatar_url": "https://avatars.githubusercontent.com/u/76624237?v=4",
234 | "events_url": "https://api.github.com/users/trufflehq/events{/privacy}",
235 | "followers_url": "https://api.github.com/users/trufflehq/followers",
236 | "following_url": "https://api.github.com/users/trufflehq/following{/other_user}",
237 | "gists_url": "https://api.github.com/users/trufflehq/gists{/gist_id}",
238 | "gravatar_id": "",
239 | "html_url": "https://github.com/trufflehq",
240 | "id": 76624237,
241 | "login": "trufflehq",
242 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2NjI0MjM3",
243 | "organizations_url": "https://api.github.com/users/trufflehq/orgs",
244 | "received_events_url": "https://api.github.com/users/trufflehq/received_events",
245 | "repos_url": "https://api.github.com/users/trufflehq/repos",
246 | "site_admin": false,
247 | "starred_url": "https://api.github.com/users/trufflehq/starred{/owner}{/repo}",
248 | "subscriptions_url": "https://api.github.com/users/trufflehq/subscriptions",
249 | "type": "Organization",
250 | "url": "https://api.github.com/users/trufflehq"
251 | }
252 | },
253 | "body": null,
254 | "closed_at": null,
255 | "comments_url": "https://api.github.com/repos/trufflehq/chonk/issues/116/comments",
256 | "commits_url": "https://api.github.com/repos/trufflehq/chonk/pulls/116/commits",
257 | "created_at": "2023-07-04T01:46:28Z",
258 | "diff_url": "https://github.com/trufflehq/chonk/pull/116.diff",
259 | "draft": false,
260 | "head": {
261 | "label": "trufflehq:szc-site-styles",
262 | "ref": "szc-site-styles",
263 | "repo": {
264 | "allow_auto_merge": false,
265 | "allow_forking": false,
266 | "allow_merge_commit": false,
267 | "allow_rebase_merge": false,
268 | "allow_squash_merge": true,
269 | "allow_update_branch": false,
270 | "archive_url": "https://api.github.com/repos/trufflehq/chonk/{archive_format}{/ref}",
271 | "archived": false,
272 | "assignees_url": "https://api.github.com/repos/trufflehq/chonk/assignees{/user}",
273 | "blobs_url": "https://api.github.com/repos/trufflehq/chonk/git/blobs{/sha}",
274 | "branches_url": "https://api.github.com/repos/trufflehq/chonk/branches{/branch}",
275 | "clone_url": "https://github.com/trufflehq/chonk.git",
276 | "collaborators_url": "https://api.github.com/repos/trufflehq/chonk/collaborators{/collaborator}",
277 | "comments_url": "https://api.github.com/repos/trufflehq/chonk/comments{/number}",
278 | "commits_url": "https://api.github.com/repos/trufflehq/chonk/commits{/sha}",
279 | "compare_url": "https://api.github.com/repos/trufflehq/chonk/compare/{base}...{head}",
280 | "contents_url": "https://api.github.com/repos/trufflehq/chonk/contents/{+path}",
281 | "contributors_url": "https://api.github.com/repos/trufflehq/chonk/contributors",
282 | "created_at": "2022-10-31T21:07:22Z",
283 | "default_branch": "main",
284 | "delete_branch_on_merge": false,
285 | "deployments_url": "https://api.github.com/repos/trufflehq/chonk/deployments",
286 | "description": "all the services! so many codes! so much chonk!",
287 | "disabled": false,
288 | "downloads_url": "https://api.github.com/repos/trufflehq/chonk/downloads",
289 | "events_url": "https://api.github.com/repos/trufflehq/chonk/events",
290 | "fork": false,
291 | "forks": 0,
292 | "forks_count": 0,
293 | "forks_url": "https://api.github.com/repos/trufflehq/chonk/forks",
294 | "full_name": "trufflehq/chonk",
295 | "git_commits_url": "https://api.github.com/repos/trufflehq/chonk/git/commits{/sha}",
296 | "git_refs_url": "https://api.github.com/repos/trufflehq/chonk/git/refs{/sha}",
297 | "git_tags_url": "https://api.github.com/repos/trufflehq/chonk/git/tags{/sha}",
298 | "git_url": "git://github.com/trufflehq/chonk.git",
299 | "has_discussions": false,
300 | "has_downloads": true,
301 | "has_issues": true,
302 | "has_pages": false,
303 | "has_projects": false,
304 | "has_wiki": false,
305 | "homepage": "",
306 | "hooks_url": "https://api.github.com/repos/trufflehq/chonk/hooks",
307 | "html_url": "https://github.com/trufflehq/chonk",
308 | "id": 560152876,
309 | "is_template": false,
310 | "issue_comment_url": "https://api.github.com/repos/trufflehq/chonk/issues/comments{/number}",
311 | "issue_events_url": "https://api.github.com/repos/trufflehq/chonk/issues/events{/number}",
312 | "issues_url": "https://api.github.com/repos/trufflehq/chonk/issues{/number}",
313 | "keys_url": "https://api.github.com/repos/trufflehq/chonk/keys{/key_id}",
314 | "labels_url": "https://api.github.com/repos/trufflehq/chonk/labels{/name}",
315 | "language": "JavaScript",
316 | "languages_url": "https://api.github.com/repos/trufflehq/chonk/languages",
317 | "license": null,
318 | "merge_commit_message": "PR_TITLE",
319 | "merge_commit_title": "MERGE_MESSAGE",
320 | "merges_url": "https://api.github.com/repos/trufflehq/chonk/merges",
321 | "milestones_url": "https://api.github.com/repos/trufflehq/chonk/milestones{/number}",
322 | "mirror_url": null,
323 | "name": "chonk",
324 | "node_id": "R_kgDOIWNBLA",
325 | "notifications_url": "https://api.github.com/repos/trufflehq/chonk/notifications{?since,all,participating}",
326 | "open_issues": 5,
327 | "open_issues_count": 5,
328 | "owner": {
329 | "avatar_url": "https://avatars.githubusercontent.com/u/76624237?v=4",
330 | "events_url": "https://api.github.com/users/trufflehq/events{/privacy}",
331 | "followers_url": "https://api.github.com/users/trufflehq/followers",
332 | "following_url": "https://api.github.com/users/trufflehq/following{/other_user}",
333 | "gists_url": "https://api.github.com/users/trufflehq/gists{/gist_id}",
334 | "gravatar_id": "",
335 | "html_url": "https://github.com/trufflehq",
336 | "id": 76624237,
337 | "login": "trufflehq",
338 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2NjI0MjM3",
339 | "organizations_url": "https://api.github.com/users/trufflehq/orgs",
340 | "received_events_url": "https://api.github.com/users/trufflehq/received_events",
341 | "repos_url": "https://api.github.com/users/trufflehq/repos",
342 | "site_admin": false,
343 | "starred_url": "https://api.github.com/users/trufflehq/starred{/owner}{/repo}",
344 | "subscriptions_url": "https://api.github.com/users/trufflehq/subscriptions",
345 | "type": "Organization",
346 | "url": "https://api.github.com/users/trufflehq"
347 | },
348 | "private": true,
349 | "pulls_url": "https://api.github.com/repos/trufflehq/chonk/pulls{/number}",
350 | "pushed_at": "2023-07-04T01:46:28Z",
351 | "releases_url": "https://api.github.com/repos/trufflehq/chonk/releases{/id}",
352 | "size": 12722,
353 | "squash_merge_commit_message": "COMMIT_MESSAGES",
354 | "squash_merge_commit_title": "COMMIT_OR_PR_TITLE",
355 | "ssh_url": "git@github.com:trufflehq/chonk.git",
356 | "stargazers_count": 2,
357 | "stargazers_url": "https://api.github.com/repos/trufflehq/chonk/stargazers",
358 | "statuses_url": "https://api.github.com/repos/trufflehq/chonk/statuses/{sha}",
359 | "subscribers_url": "https://api.github.com/repos/trufflehq/chonk/subscribers",
360 | "subscription_url": "https://api.github.com/repos/trufflehq/chonk/subscription",
361 | "svn_url": "https://github.com/trufflehq/chonk",
362 | "tags_url": "https://api.github.com/repos/trufflehq/chonk/tags",
363 | "teams_url": "https://api.github.com/repos/trufflehq/chonk/teams",
364 | "topics": [],
365 | "trees_url": "https://api.github.com/repos/trufflehq/chonk/git/trees{/sha}",
366 | "updated_at": "2023-06-09T21:26:41Z",
367 | "url": "https://api.github.com/repos/trufflehq/chonk",
368 | "use_squash_pr_title_as_default": false,
369 | "visibility": "private",
370 | "watchers": 2,
371 | "watchers_count": 2,
372 | "web_commit_signoff_required": false
373 | },
374 | "sha": "c71e4dfab43e7ede9919e5adb51a717366c798cc",
375 | "user": {
376 | "avatar_url": "https://avatars.githubusercontent.com/u/76624237?v=4",
377 | "events_url": "https://api.github.com/users/trufflehq/events{/privacy}",
378 | "followers_url": "https://api.github.com/users/trufflehq/followers",
379 | "following_url": "https://api.github.com/users/trufflehq/following{/other_user}",
380 | "gists_url": "https://api.github.com/users/trufflehq/gists{/gist_id}",
381 | "gravatar_id": "",
382 | "html_url": "https://github.com/trufflehq",
383 | "id": 76624237,
384 | "login": "trufflehq",
385 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2NjI0MjM3",
386 | "organizations_url": "https://api.github.com/users/trufflehq/orgs",
387 | "received_events_url": "https://api.github.com/users/trufflehq/received_events",
388 | "repos_url": "https://api.github.com/users/trufflehq/repos",
389 | "site_admin": false,
390 | "starred_url": "https://api.github.com/users/trufflehq/starred{/owner}{/repo}",
391 | "subscriptions_url": "https://api.github.com/users/trufflehq/subscriptions",
392 | "type": "Organization",
393 | "url": "https://api.github.com/users/trufflehq"
394 | }
395 | },
396 | "html_url": "https://github.com/trufflehq/chonk/pull/116",
397 | "id": 1418846518,
398 | "issue_url": "https://api.github.com/repos/trufflehq/chonk/issues/116",
399 | "labels": [],
400 | "locked": false,
401 | "merge_commit_sha": null,
402 | "merged_at": null,
403 | "milestone": null,
404 | "node_id": "PR_kwDOIWNBLM5UkeE2",
405 | "number": 116,
406 | "patch_url": "https://github.com/trufflehq/chonk/pull/116.patch",
407 | "requested_reviewers": [],
408 | "requested_teams": [],
409 | "review_comment_url": "https://api.github.com/repos/trufflehq/chonk/pulls/comments{/number}",
410 | "review_comments_url": "https://api.github.com/repos/trufflehq/chonk/pulls/116/comments",
411 | "state": "open",
412 | "statuses_url": "https://api.github.com/repos/trufflehq/chonk/statuses/c71e4dfab43e7ede9919e5adb51a717366c798cc",
413 | "title": "App.truffle.vip styles for profile page and community page",
414 | "updated_at": "2023-07-04T07:52:40Z",
415 | "url": "https://api.github.com/repos/trufflehq/chonk/pulls/116",
416 | "user": {
417 | "avatar_url": "https://avatars.githubusercontent.com/u/70922464?v=4",
418 | "events_url": "https://api.github.com/users/shanecranor/events{/privacy}",
419 | "followers_url": "https://api.github.com/users/shanecranor/followers",
420 | "following_url": "https://api.github.com/users/shanecranor/following{/other_user}",
421 | "gists_url": "https://api.github.com/users/shanecranor/gists{/gist_id}",
422 | "gravatar_id": "",
423 | "html_url": "https://github.com/shanecranor",
424 | "id": 70922464,
425 | "login": "shanecranor",
426 | "node_id": "MDQ6VXNlcjcwOTIyNDY0",
427 | "organizations_url": "https://api.github.com/users/shanecranor/orgs",
428 | "received_events_url": "https://api.github.com/users/shanecranor/received_events",
429 | "repos_url": "https://api.github.com/users/shanecranor/repos",
430 | "site_admin": false,
431 | "starred_url": "https://api.github.com/users/shanecranor/starred{/owner}{/repo}",
432 | "subscriptions_url": "https://api.github.com/users/shanecranor/subscriptions",
433 | "type": "User",
434 | "url": "https://api.github.com/users/shanecranor"
435 | }
436 | },
437 | "repository": {
438 | "allow_forking": false,
439 | "archive_url": "https://api.github.com/repos/trufflehq/chonk/{archive_format}{/ref}",
440 | "archived": false,
441 | "assignees_url": "https://api.github.com/repos/trufflehq/chonk/assignees{/user}",
442 | "blobs_url": "https://api.github.com/repos/trufflehq/chonk/git/blobs{/sha}",
443 | "branches_url": "https://api.github.com/repos/trufflehq/chonk/branches{/branch}",
444 | "clone_url": "https://github.com/trufflehq/chonk.git",
445 | "collaborators_url": "https://api.github.com/repos/trufflehq/chonk/collaborators{/collaborator}",
446 | "comments_url": "https://api.github.com/repos/trufflehq/chonk/comments{/number}",
447 | "commits_url": "https://api.github.com/repos/trufflehq/chonk/commits{/sha}",
448 | "compare_url": "https://api.github.com/repos/trufflehq/chonk/compare/{base}...{head}",
449 | "contents_url": "https://api.github.com/repos/trufflehq/chonk/contents/{+path}",
450 | "contributors_url": "https://api.github.com/repos/trufflehq/chonk/contributors",
451 | "created_at": "2022-10-31T21:07:22Z",
452 | "default_branch": "main",
453 | "deployments_url": "https://api.github.com/repos/trufflehq/chonk/deployments",
454 | "description": "all the services! so many codes! so much chonk!",
455 | "disabled": false,
456 | "downloads_url": "https://api.github.com/repos/trufflehq/chonk/downloads",
457 | "events_url": "https://api.github.com/repos/trufflehq/chonk/events",
458 | "fork": false,
459 | "forks": 0,
460 | "forks_count": 0,
461 | "forks_url": "https://api.github.com/repos/trufflehq/chonk/forks",
462 | "full_name": "trufflehq/chonk",
463 | "git_commits_url": "https://api.github.com/repos/trufflehq/chonk/git/commits{/sha}",
464 | "git_refs_url": "https://api.github.com/repos/trufflehq/chonk/git/refs{/sha}",
465 | "git_tags_url": "https://api.github.com/repos/trufflehq/chonk/git/tags{/sha}",
466 | "git_url": "git://github.com/trufflehq/chonk.git",
467 | "has_discussions": false,
468 | "has_downloads": true,
469 | "has_issues": true,
470 | "has_pages": false,
471 | "has_projects": false,
472 | "has_wiki": false,
473 | "homepage": "",
474 | "hooks_url": "https://api.github.com/repos/trufflehq/chonk/hooks",
475 | "html_url": "https://github.com/trufflehq/chonk",
476 | "id": 560152876,
477 | "is_template": false,
478 | "issue_comment_url": "https://api.github.com/repos/trufflehq/chonk/issues/comments{/number}",
479 | "issue_events_url": "https://api.github.com/repos/trufflehq/chonk/issues/events{/number}",
480 | "issues_url": "https://api.github.com/repos/trufflehq/chonk/issues{/number}",
481 | "keys_url": "https://api.github.com/repos/trufflehq/chonk/keys{/key_id}",
482 | "labels_url": "https://api.github.com/repos/trufflehq/chonk/labels{/name}",
483 | "language": "JavaScript",
484 | "languages_url": "https://api.github.com/repos/trufflehq/chonk/languages",
485 | "license": null,
486 | "merges_url": "https://api.github.com/repos/trufflehq/chonk/merges",
487 | "milestones_url": "https://api.github.com/repos/trufflehq/chonk/milestones{/number}",
488 | "mirror_url": null,
489 | "name": "chonk",
490 | "node_id": "R_kgDOIWNBLA",
491 | "notifications_url": "https://api.github.com/repos/trufflehq/chonk/notifications{?since,all,participating}",
492 | "open_issues": 5,
493 | "open_issues_count": 5,
494 | "owner": {
495 | "avatar_url": "https://avatars.githubusercontent.com/u/76624237?v=4",
496 | "events_url": "https://api.github.com/users/trufflehq/events{/privacy}",
497 | "followers_url": "https://api.github.com/users/trufflehq/followers",
498 | "following_url": "https://api.github.com/users/trufflehq/following{/other_user}",
499 | "gists_url": "https://api.github.com/users/trufflehq/gists{/gist_id}",
500 | "gravatar_id": "",
501 | "html_url": "https://github.com/trufflehq",
502 | "id": 76624237,
503 | "login": "trufflehq",
504 | "node_id": "MDEyOk9yZ2FuaXphdGlvbjc2NjI0MjM3",
505 | "organizations_url": "https://api.github.com/users/trufflehq/orgs",
506 | "received_events_url": "https://api.github.com/users/trufflehq/received_events",
507 | "repos_url": "https://api.github.com/users/trufflehq/repos",
508 | "site_admin": false,
509 | "starred_url": "https://api.github.com/users/trufflehq/starred{/owner}{/repo}",
510 | "subscriptions_url": "https://api.github.com/users/trufflehq/subscriptions",
511 | "type": "Organization",
512 | "url": "https://api.github.com/users/trufflehq"
513 | },
514 | "private": true,
515 | "pulls_url": "https://api.github.com/repos/trufflehq/chonk/pulls{/number}",
516 | "pushed_at": "2023-07-04T01:46:28Z",
517 | "releases_url": "https://api.github.com/repos/trufflehq/chonk/releases{/id}",
518 | "size": 12722,
519 | "ssh_url": "git@github.com:trufflehq/chonk.git",
520 | "stargazers_count": 2,
521 | "stargazers_url": "https://api.github.com/repos/trufflehq/chonk/stargazers",
522 | "statuses_url": "https://api.github.com/repos/trufflehq/chonk/statuses/{sha}",
523 | "subscribers_url": "https://api.github.com/repos/trufflehq/chonk/subscribers",
524 | "subscription_url": "https://api.github.com/repos/trufflehq/chonk/subscription",
525 | "svn_url": "https://github.com/trufflehq/chonk",
526 | "tags_url": "https://api.github.com/repos/trufflehq/chonk/tags",
527 | "teams_url": "https://api.github.com/repos/trufflehq/chonk/teams",
528 | "topics": [],
529 | "trees_url": "https://api.github.com/repos/trufflehq/chonk/git/trees{/sha}",
530 | "updated_at": "2023-06-09T21:26:41Z",
531 | "url": "https://api.github.com/repos/trufflehq/chonk",
532 | "visibility": "private",
533 | "watchers": 2,
534 | "watchers_count": 2,
535 | "web_commit_signoff_required": false
536 | },
537 | "sender": {
538 | "avatar_url": "https://avatars.githubusercontent.com/u/1271767?v=4",
539 | "events_url": "https://api.github.com/users/austinhallock/events{/privacy}",
540 | "followers_url": "https://api.github.com/users/austinhallock/followers",
541 | "following_url": "https://api.github.com/users/austinhallock/following{/other_user}",
542 | "gists_url": "https://api.github.com/users/austinhallock/gists{/gist_id}",
543 | "gravatar_id": "",
544 | "html_url": "https://github.com/austinhallock",
545 | "id": 1271767,
546 | "login": "austinhallock",
547 | "node_id": "MDQ6VXNlcjEyNzE3Njc=",
548 | "organizations_url": "https://api.github.com/users/austinhallock/orgs",
549 | "received_events_url": "https://api.github.com/users/austinhallock/received_events",
550 | "repos_url": "https://api.github.com/users/austinhallock/repos",
551 | "site_admin": false,
552 | "starred_url": "https://api.github.com/users/austinhallock/starred{/owner}{/repo}",
553 | "subscriptions_url": "https://api.github.com/users/austinhallock/subscriptions",
554 | "type": "User",
555 | "url": "https://api.github.com/users/austinhallock"
556 | }
557 | }
558 |
--------------------------------------------------------------------------------
/chuckle-github/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod pull_request_review_comment;
2 |
3 | const CHUCKLE_USER_AGENT: &str = concat!(
4 | env!("CARGO_PKG_NAME"),
5 | "/",
6 | env!("CARGO_PKG_VERSION"),
7 | " (",
8 | env!("CARGO_PKG_HOMEPAGE"),
9 | ")"
10 | );
11 |
12 | /// Fetches a raw file fro githubusercontent.com
13 | pub async fn fetch_raw_file(
14 | token: String,
15 | owner: String,
16 | name: String,
17 | commit: String,
18 | path: String,
19 | ) -> anyhow::Result {
20 | let file_url =
21 | format!("https://api.github.com/repos/{owner}/{name}/contents/{path}?ref={commit}",);
22 |
23 | let client = reqwest::Client::new();
24 | let resp = client
25 | .get(&file_url)
26 | .header("Authorization", format!("token {token}"))
27 | .header("User-Agent", CHUCKLE_USER_AGENT)
28 | .header("Accept", "application/vnd.github.raw")
29 | .send()
30 | .await?;
31 |
32 | let body = resp.text().await?;
33 |
34 | Ok(body)
35 | }
36 |
37 | #[cfg(test)]
38 | mod test {
39 | #[tokio::test]
40 | async fn test_fetch_raw_file() {
41 | let token = std::env::var("GITHUB_TOKEN").unwrap();
42 | let owner = std::env::var("GITHUB_OWNER").unwrap_or("trufflehq".into());
43 | let name = std::env::var("GITHUB_REPO").unwrap_or("chuckle".into());
44 | let commit = std::env::var("GITHUB_COMMIT").unwrap_or("HEAD".into());
45 | let path = std::env::var("GITHUB_PATH").unwrap_or("README.md".into());
46 |
47 | let file = super::fetch_raw_file(token, owner, name, commit, path).await;
48 | assert!(file.is_ok());
49 | let file = file.unwrap();
50 |
51 | assert!(!file.is_empty());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/chuckle-github/src/pull_request_review_comment.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 |
3 | #[derive(Debug, Clone, Serialize, Deserialize)]
4 | pub struct PullRequestReviewComment {
5 | pub action: String,
6 | pub comment: Comment,
7 | pub pull_request: PullRequest,
8 | pub repository: Repo,
9 | pub organization: Organization,
10 | pub sender: Sender,
11 | }
12 |
13 | #[derive(Debug, Clone, Serialize, Deserialize)]
14 | pub struct Comment {
15 | pub url: String,
16 | pub pull_request_review_id: i64,
17 | pub id: i64,
18 | pub node_id: String,
19 | pub diff_hunk: String,
20 | pub path: String,
21 | pub commit_id: String,
22 | pub original_commit_id: String,
23 | pub user: Sender,
24 | pub body: String,
25 | pub created_at: String,
26 | pub updated_at: String,
27 | pub html_url: String,
28 | pub pull_request_url: String,
29 | pub author_association: String,
30 | #[serde(rename = "_links")]
31 | pub links: CommentLinks,
32 | pub reactions: Reactions,
33 | pub start_line: Option,
34 | pub original_start_line: Option,
35 | pub start_side: Option,
36 | pub line: Option,
37 | pub original_line: i64,
38 | pub side: String,
39 | pub original_position: i64,
40 | pub position: i64,
41 | pub subject_type: String,
42 | }
43 |
44 | #[derive(Debug, Clone, Serialize, Deserialize)]
45 | pub struct CommentLinks {
46 | #[serde(rename = "self")]
47 | pub links_self: Html,
48 | pub html: Html,
49 | pub pull_request: Html,
50 | }
51 |
52 | #[derive(Debug, Clone, Serialize, Deserialize)]
53 | pub struct Html {
54 | pub href: String,
55 | }
56 |
57 | #[derive(Debug, Clone, Serialize, Deserialize)]
58 | pub struct Reactions {
59 | pub url: String,
60 | pub total_count: i64,
61 | #[serde(rename = "+1")]
62 | pub the_1: i64,
63 | #[serde(rename = "-1")]
64 | pub reactions_1: i64,
65 | pub laugh: i64,
66 | pub hooray: i64,
67 | pub confused: i64,
68 | pub heart: i64,
69 | pub rocket: i64,
70 | pub eyes: i64,
71 | }
72 |
73 | #[derive(Debug, Clone, Serialize, Deserialize)]
74 | pub struct Sender {
75 | pub login: String,
76 | pub id: i64,
77 | pub node_id: String,
78 | pub avatar_url: String,
79 | pub gravatar_id: String,
80 | pub url: String,
81 | pub html_url: String,
82 | pub followers_url: String,
83 | pub following_url: String,
84 | pub gists_url: String,
85 | pub starred_url: String,
86 | pub subscriptions_url: String,
87 | pub organizations_url: String,
88 | pub repos_url: String,
89 | pub events_url: String,
90 | pub received_events_url: String,
91 | #[serde(rename = "type")]
92 | pub sender_type: String,
93 | pub site_admin: bool,
94 | }
95 |
96 | #[derive(Debug, Clone, Serialize, Deserialize)]
97 | pub struct Organization {
98 | pub login: String,
99 | pub id: i64,
100 | pub node_id: String,
101 | pub url: String,
102 | pub repos_url: String,
103 | pub events_url: String,
104 | pub hooks_url: String,
105 | pub issues_url: String,
106 | pub members_url: String,
107 | pub public_members_url: String,
108 | pub avatar_url: String,
109 | pub description: String,
110 | }
111 |
112 | #[derive(Debug, Clone, Serialize, Deserialize)]
113 | pub struct PullRequest {
114 | pub url: String,
115 | pub id: i64,
116 | pub node_id: String,
117 | pub html_url: String,
118 | pub diff_url: String,
119 | pub patch_url: String,
120 | pub issue_url: String,
121 | pub number: i64,
122 | pub state: String,
123 | pub locked: bool,
124 | pub title: String,
125 | pub user: Sender,
126 | pub body: Option,
127 | pub created_at: String,
128 | pub updated_at: String,
129 | pub closed_at: Option,
130 | pub merged_at: Option,
131 | pub merge_commit_sha: Option,
132 | pub assignee: Option,
133 | pub assignees: Vec