feat: add multi-lang support and cohort scripts (#1)
CI / dependency-pin-check-typescript (push) Successful in 4s
CI / dependency-pin-check-python (push) Successful in 3s
CI / typescript (push) Successful in 9m38s
CI / python (push) Successful in 9m23s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m6s

### Explanation

_No response_

### Issue

_No response_

### Attestations

- [ ] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [ ] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [ ] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [ ] I have pinned the dependencies to a specific patch version.

### Style

- [ ] I have run the linter and resolved any errors.
- [ ] My pull request uses an appropriate title, matching the conventional commit standards.
- [ ] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

_No response_

Co-authored-by: Hikari <hikari@nhcarrigan.com>
Reviewed-on: #1
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #1.
This commit is contained in:
2026-01-23 20:07:16 -08:00
committed by Naomi Carrigan
parent 38e7f15d93
commit 6b5fa40599
59 changed files with 2249 additions and 48 deletions
+188
View File
@@ -0,0 +1,188 @@
/* eslint-disable @typescript-eslint/naming-convention -- The names of the properties match the API responses. */
/**
* @copyright NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
interface CategoryList {
category_list: {
can_create_category: boolean;
can_create_topic: boolean;
categories: Array<{
id: number;
name: string;
color: string;
text_color: string;
style_type: string;
emoji: string;
icon: string;
slug: string;
topic_count: number;
post_count: number;
position: number;
description: string;
description_text: string;
description_excerpt: string;
topic_url: string;
read_restricted: boolean;
permission: number;
notification_level: number;
can_edit: boolean;
topic_template: string;
has_children: boolean;
subcategory_count: number;
sort_order: string;
sort_ascending: string;
show_subcategory_list: boolean;
num_featured_topics: number;
default_view: string;
subcategory_list_style: string;
default_top_period: string;
default_list_filter: string;
minimum_required_tags: number;
navigate_to_first_post_after_read: boolean;
topics_day: number;
topics_week: number;
topics_month: number;
topics_year: number;
topics_all_time: number;
is_uncategorized: boolean;
subcategory_ids: Array<number>;
subcategory_list: Array<{
id: number;
name: string;
}>;
uploaded_logo: string;
uploaded_logo_dark: string;
uploaded_background: string;
uploaded_background_dark: string;
}>;
};
}
interface CategoryGet {
category?: {
id: number;
name: string;
color: string;
text_color: string;
style_type: string;
emoji: string;
icon: string;
slug: string;
topic_count: number;
post_count: number;
position: number;
description: string;
description_text: string;
description_excerpt: string;
topic_url: string;
read_restricted: boolean;
permission: number;
notification_level: number;
can_edit: boolean;
topic_template: string;
form_template_ids: Array<unknown>;
has_children: boolean;
subcategory_count: number;
sort_order: string;
sort_ascending: string;
show_subcategory_list: boolean;
num_featured_topics: number;
default_view: string;
subcategory_list_style: string;
default_top_period: string;
default_list_filter: string;
minimum_required_tags: number;
navigate_to_first_post_after_read: boolean;
custom_fields: Record<string, unknown>;
allowed_tags: Array<unknown>;
allowed_tag_groups: Array<unknown>;
allow_global_tags: boolean;
required_tag_groups: Array<{
name: string;
min_count: number;
}>;
category_setting: {
auto_bump_cooldown_days: number;
num_auto_bump_daily: number;
require_reply_approval: boolean;
require_topic_approval: boolean;
};
category_localizations: Array<unknown>;
read_only_banner: string;
available_groups: Array<unknown>;
auto_close_hours: string;
auto_close_based_on_last_post: boolean;
allow_unlimited_owner_edits_on_first_post: boolean;
default_slow_mode_seconds: string;
group_permissions: Array<{
permission_type: number;
group_name: string;
group_id: number;
}>;
email_in: string;
email_in_allow_strangers: boolean;
mailinglist_mirror: boolean;
all_topics_wiki: boolean;
can_delete: boolean;
allow_badges: boolean;
topic_featured_link_allowed: boolean;
search_priority: number;
uploaded_logo: string;
uploaded_logo_dark: string;
uploaded_background: string;
uploaded_background_dark: string;
};
}
interface Topic {
id: number;
title: string;
fancy_title: string;
slug: string;
posts_count: number;
reply_count: number;
highest_post_number: number;
image_url: string | null;
created_at: string;
last_posted_at: string | null;
bumped: boolean;
bumped_at: string | null;
archetype: string;
unseen: boolean;
pinned: boolean;
unpinned: boolean | null;
visible: boolean;
closed: boolean;
archived: boolean;
bookmarked: boolean | null;
liked: boolean | null;
tags: Array<string>;
tags_descriptions: Record<string, string>;
like_count: number;
views: number;
category_id: number;
featured_link: string | null;
has_accepted_answer: boolean;
posters: Array<{
extras: string | null;
description: string;
user_id: number;
primary_group_id: number | null;
}>;
}
interface TopicList {
topic_list: {
can_create_topic: boolean;
draft: string | null;
draft_key: string;
draft_sequence: number;
per_page: number;
topics: Array<Topic>;
};
}
export type { CategoryList, CategoryGet, Topic, TopicList };
+37
View File
@@ -0,0 +1,37 @@
/* eslint-disable @typescript-eslint/naming-convention -- interface properties match API responses. */
/**
* @copyright NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
interface Product {
id: number;
name: string;
}
interface Finding extends Record<string, unknown> {
id: number;
title: string;
severity: "Critical" | "High" | "Medium" | "Low" | "Info";
active: boolean;
verified: boolean;
product?: Product;
}
interface DojoResponse {
count: number;
next: string | null;
previous: string | null;
results: Array<Finding>;
}
interface ProjectStats {
Critical: number;
High: number;
Medium: number;
Low: number;
Info: number;
}
export type { Product, Finding, DojoResponse, ProjectStats };
+216
View File
@@ -0,0 +1,216 @@
/**
* @copyright NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
/* eslint-disable @typescript-eslint/naming-convention -- This is a Gitea interface. The responses from the API are not something we can control. */
interface Repository extends Record<string, unknown> {
allow_fast_forward_only_merge: boolean;
allow_merge_commits: boolean;
allow_rebase: boolean;
allow_rebase_explicit: boolean;
allow_rebase_update: boolean;
allow_squash_merge: boolean;
archived: boolean;
archived_at: string;
avatar_url: string;
clone_url: string;
created_at: string;
default_allow_maintainer_edit: boolean;
default_branch: string;
default_delete_branch_after_merge: boolean;
default_merge_style: string;
description: string;
empty: boolean;
external_tracker: {
external_tracker_format: string;
external_tracker_regexp_pattern: string;
external_tracker_style: string;
external_tracker_url: string;
};
external_wiki: {
external_wiki_url: string;
};
fork: boolean;
forks_count: number;
full_name: string;
has_actions: boolean;
has_issues: boolean;
has_packages: boolean;
has_projects: boolean;
has_pull_requests: boolean;
has_releases: boolean;
has_wiki: boolean;
html_url: string;
id: number;
ignore_whitespace_conflicts: boolean;
internal: boolean;
internal_tracker: {
allow_only_contributors_to_track_time: boolean;
enable_issue_dependencies: boolean;
enable_time_tracker: boolean;
};
language: string;
languages_url: string;
licenses: Array<string>;
link: string;
mirror: boolean;
mirror_interval: string;
mirror_updated: string;
name: string;
object_format_name: string;
open_issues_count: number;
open_pr_counter: number;
original_url: string;
owner: {
active: boolean;
avatar_url: string;
created: string;
description: string;
email: string;
followers_count: number;
following_count: number;
full_name: string;
html_url: string;
id: number;
is_admin: boolean;
language: string;
last_login: string;
location: string;
login: string;
login_name: string;
prohibit_login: boolean;
restricted: boolean;
source_id: number;
starred_repos_count: number;
visibility: string;
website: string;
};
parent: string;
permissions: {
admin: boolean;
pull: boolean;
push: boolean;
};
private: boolean;
projects_mode: string;
release_counter: number;
repo_transfer: {
doer: {
active: boolean;
avatar_url: string;
created: string;
description: string;
email: string;
followers_count: number;
following_count: number;
full_name: string;
html_url: string;
id: number;
is_admin: boolean;
language: string;
last_login: string;
location: string;
login: string;
login_name: string;
prohibit_login: boolean;
restricted: boolean;
source_id: number;
starred_repos_count: number;
visibility: string;
website: string;
};
recipient: {
active: boolean;
avatar_url: string;
created: string;
description: string;
email: string;
followers_count: number;
following_count: number;
full_name: string;
html_url: string;
id: number;
is_admin: boolean;
language: string;
last_login: string;
location: string;
login: string;
login_name: string;
prohibit_login: boolean;
restricted: boolean;
source_id: number;
starred_repos_count: number;
visibility: string;
website: string;
};
teams: Array<{
can_create_org_repo: boolean;
description: string;
id: number;
includes_all_repositories: boolean;
name: string;
organization: {
avatar_url: string;
description: string;
email: string;
full_name: string;
id: number;
location: string;
name: string;
repo_admin_change_team_access: boolean;
username: string;
visibility: string;
website: string;
};
permission: string;
units: Array<string>;
units_map: {
"repo.code": string;
"repo.ext_issues": string;
"repo.ext_wiki": string;
"repo.issues": string;
"repo.projects": string;
"repo.pulls": string;
"repo.releases": string;
"repo.wiki": string;
};
}>;
};
size: number;
ssh_url: string;
stars_count: number;
template: boolean;
topics: Array<string>;
updated_at: string;
url: string;
watchers_count: number;
website: string;
}
interface File {
_links: {
git: string;
html: string;
self: string;
};
content: string;
download_url: string;
encoding: string;
git_url: string;
html_url: string;
last_author_date: string;
last_commit_sha: string;
last_committer_date: string;
name: string;
path: string;
sha: string;
size: number;
submodule_git_url: string;
target: string;
type: string;
url: string;
}
export type { Repository, File };