Initial work on quests

This commit is contained in:
Puyodead1
2025-12-24 23:46:38 -05:00
parent 55b3416957
commit 724ebe1792
54 changed files with 2039 additions and 9 deletions

View File

@@ -51,6 +51,8 @@ export const NO_AUTHORIZATION_ROUTES = [
"GET /policies/instance/",
// Oauth callback
"/oauth2/callback",
// get quest config
/^GET \/quests\/\d+/,
// Asset delivery
/^(GET|HEAD) \/guilds\/\d+\/widget\.(json|png)/,
/^(GET|HEAD) \/guilds\/\d+\/shield\.svg/,

View File

@@ -0,0 +1,86 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import { EntitlementSpecialSourceType, EntitlementType, QuestClaimRewardRequestSchema, QuestClaimRewardResponseSchema } from "@spacebar/schemas/quests";
import { emitEvent } from "@spacebar/util";
import { Request, Response, Router } from "express";
const router = Router({ mergeParams: true });
router.post(
"/",
route({
description:
"Claims the quest's rewards, setting the completed_at and claimed_at fields of the quest user status to the current timestamp. Fires a Quests User Status Update Gateway event.",
requestBody: "QuestClaimRewardRequestSchema",
responses: {
200: {
body: "QuestClaimRewardResponseSchema",
},
},
}),
async (req: Request, res: Response) => {
const { quest_id } = req.params;
const { location, platform } = req.body as QuestClaimRewardRequestSchema;
// TODO: implement
console.debug(`POST /quests/${quest_id}/claim-reward is incomplete`);
await emitEvent({
event: "QUESTS_USER_STATUS_UPDATE",
data: {
user_status: {
claimed_at: null,
claimed_tier: null,
completed_at: null,
dismissed_quest_content: 0,
enrolled_at: new Date().toISOString(),
last_stream_heartbeat_at: null,
progress: {},
quest_id,
stream_progress_seconds: 0,
user_id: req.user.id,
},
},
});
res.json({
claimed_at: new Date().toISOString(),
entitlement_expiration_metadata: {},
entitlements: [
{
id: "1453600390154162249",
sku_id: "1287881739531976815",
application_id: "1287870191526613112",
user_id: req.user.id,
deleted: false,
starts_at: null,
ends_at: null,
type: EntitlementType.QUEST_REWARD,
tenant_metadata: {},
source_type: EntitlementSpecialSourceType.QUEST_REWARD,
gift_code_flags: 0, // PAYMENT_SOURCE_REQUIRED, todo: make a bitfield enum
promotion_id: null,
},
],
errors: [],
} as QuestClaimRewardResponseSchema);
},
);
export default router;

View File

@@ -0,0 +1,62 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import { QuestEnrollRequestSchema, QuestUserStatusSchema } from "@spacebar/schemas";
import { emitEvent } from "@spacebar/util";
import { Request, Response, Router } from "express";
const router = Router({ mergeParams: true });
router.post(
"/",
route({
description: "Accepts a quest and returns a quest user status object. Fires a Quests User Status Update Gateway event.",
requestBody: "QuestEnrollRequestSchema",
}),
async (req: Request, res: Response) => {
const { quest_id } = req.params;
const { location } = req.body as QuestEnrollRequestSchema;
// TODO: implement
console.debug(`POST /quests/${quest_id}/enroll is incomplete`);
const status: QuestUserStatusSchema = {
claimed_at: null,
claimed_tier: null,
completed_at: null,
dismissed_quest_content: 0,
enrolled_at: new Date().toISOString(),
last_stream_heartbeat_at: null,
progress: {},
quest_id,
stream_progress_seconds: 0,
user_id: req.user.id,
};
await emitEvent({
event: "QUESTS_USER_STATUS_UPDATE",
data: {
user_status: status,
},
});
res.json(status);
},
);
export default router;

View File

@@ -0,0 +1,141 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import { QuestAssignmentMethod, QuestConfigResponseSchema, QuestEventType, QuestFeature, QuestPlatformType, QuestRewardType, QuestSharePolicy } from "@spacebar/schemas";
import { Request, Response, Router } from "express";
const router = Router({ mergeParams: true });
router.get(
"/",
route({
description: "Returns a quest config object for the specified quest. Quest must be currently active.",
responses: {
200: {
body: "QuestConfigResponseSchema",
},
},
}),
async (req: Request, res: Response) => {
const { quest_id } = req.params;
// TODO: implement
console.debug(`GET /quests/${quest_id} is incomplete`);
// just a dummy response for now
res.json({
id: "1333839522189938740",
config_version: 2,
starts_at: "2025-07-14T14:00:00+00:00",
expires_at: "2099-08-15T23:00:00+00:00",
features: [QuestFeature.QUEST_BAR_V2, QuestFeature.REWARD_HIGHLIGHTING, QuestFeature.DISMISSAL_SURVEY, QuestFeature.QUESTS_CDN, QuestFeature.PACING_CONTROLLER],
application: {
link: "https://spacebar.chat",
id: "545364944258990091",
name: "Spacebar",
},
colors: {
primary: "#5865F2",
secondary: "#000000",
},
assets: {
hero: "quests/1333839522189938740/orbs_quest_card_banner_4.jpeg",
hero_video: null,
quest_bar_hero: "quests/1333839522189938740/orbs_quest_bar.png",
quest_bar_hero_video: null,
game_tile: "discord_game_tile.png",
logotype: "discord_logo.png",
game_tile_light: "quests/1333839522189938740/1417603112168067182.png",
game_tile_dark: "quests/1333839522189938740/1417603112742551603.png",
logotype_light: "quests/1333839522189938740/1417603113304719540.png",
logotype_dark: "quests/1333839522189938740/1417603113791131668.png",
},
messages: {
quest_name: "Spacebar Bars Intro",
game_title: "Spacebar",
game_publisher: "Spacebar",
},
task_config_v2: {
tasks: {
[QuestEventType.WATCH_VIDEO]: {
type: QuestEventType.WATCH_VIDEO,
target: 31,
assets: {
video: {
url: "quests/1410358070831480904/1420884840815005717_1080.mp4",
width: 1080,
height: 1920,
thumbnail: "quests/1410358070831480904/1421253196549984267.png",
caption: "quests/1410358070831480904/1410370389451866112.vtt",
transcript: "quests/1410358070831480904/1410370413032374293.txt",
},
video_low_res: {
url: "quests/1410358070831480904/1420884840815005717_720.mp4",
width: 1080,
height: 1920,
thumbnail: "quests/1410358070831480904/1421253196549984267.png",
caption: "quests/1410358070831480904/1410370389451866112.vtt",
transcript: "quests/1410358070831480904/1410370413032374293.txt",
},
video_hls: {
url: "quests/1410358070831480904/1420884840815005717.m3u8",
width: 1080,
height: 1920,
thumbnail: "quests/1410358070831480904/1421253196549984267.png",
caption: "quests/1410358070831480904/1410370389451866112.vtt",
transcript: "quests/1410358070831480904/1410370413032374293.txt",
},
},
messages: {
video_title: "Intro to Spacebar Bars",
// video_end_cta_title: "Learn more about Bars",
// video_end_cta_subtitle: "Head to our Help Center for more information.",
},
},
},
join_operator: "or",
},
rewards_config: {
assignment_method: QuestAssignmentMethod.ALL,
rewards: [
{
type: QuestRewardType.VIRTUAL_CURRENCY,
sku_id: "1287881739531976815",
messages: {
name: "150 Bars",
name_with_article: "150 Bars",
redemption_instructions_by_platform: {
[QuestPlatformType.CROSS_PLATFORM]: "Default",
},
},
orb_quantity: 150,
},
],
rewards_expire_at: "2099-02-01T00:00:27+00:00",
platforms: [QuestPlatformType.CROSS_PLATFORM],
},
share_policy: QuestSharePolicy.SHAREABLE_EVERYWHERE,
cta_config: {
link: "https://spacebar.chat",
button_label: "Learn More",
subtitle: "Head to our Help Center for more information.",
},
} as QuestConfigResponseSchema);
},
);
export default router;

View File

@@ -0,0 +1,50 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import { QuestPlatformType, QuestRewardCodeResponseSchema } from "@spacebar/schemas/quests";
import { Request, Response, Router } from "express";
const router = Router({ mergeParams: true });
router.get(
"/",
route({
description: "Retrieves the reward code for the specified platform. Returns a quest reward code object on success.",
responses: {
200: {
body: "QuestRewardCodeResponseSchema",
},
},
}),
async (req: Request, res: Response) => {
const { quest_id } = req.params;
// TODO: implement
console.debug(`GET /quests/${quest_id}/reward-code is incomplete`);
res.json({
quest_id,
claimed_at: "2025-08-01T12:00:00+00:00",
code: "REWARD-CODE-1234",
platform: QuestPlatformType.CROSS_PLATFORM,
user_id: req.user.id,
} as QuestRewardCodeResponseSchema);
},
);
export default router;

View File

@@ -0,0 +1,68 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import { QuestUserStatusSchema, QuestVideoProgressRequestSchema } from "@spacebar/schemas/quests";
import { emitEvent } from "@spacebar/util";
import { Request, Response, Router } from "express";
const router = Router({ mergeParams: true });
router.post(
"/",
route({
description:
"Tells the server to update the value field of the current video task. Used for keeping track of how long the video has been watched for, and for checking if the user has met the task duration requirement. Returns a quest user status object on success. Fires a Quests User Status Update Gateway event.",
requestBody: "QuestVideoProgressRequestSchema",
responses: {
200: {
body: "QuestVideoProgressResponseSchema",
},
},
}),
async (req: Request, res: Response) => {
const { quest_id } = req.params;
const { timestamp } = req.body as QuestVideoProgressRequestSchema;
// TODO: implement
console.debug(`POST /quests/${quest_id}/video-progress is incomplete`);
const status: QuestUserStatusSchema = {
claimed_at: null,
claimed_tier: null,
completed_at: null,
dismissed_quest_content: 0,
enrolled_at: new Date().toISOString(),
last_stream_heartbeat_at: null,
progress: {},
quest_id,
stream_progress_seconds: 0,
user_id: req.user.id,
};
await emitEvent({
event: "QUESTS_USER_STATUS_UPDATE",
data: {
user_status: status,
},
});
res.json(status);
},
);
export default router;

View File

@@ -0,0 +1,340 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import { ClaimedQuestsResponseSchema, QuestAssignmentMethod, QuestEventType, QuestFeature, QuestPlatformType, QuestRewardType, QuestSharePolicy } from "@spacebar/schemas";
import { QuestsResponseSchema } from "@spacebar/schemas/responses";
import { Request, Response, Router } from "express";
const router = Router({ mergeParams: true });
router.get(
"/",
route({
description: "Returns information on the current quests for the current user.",
responses: {
200: {
body: "QuestsResponseSchema",
},
},
}),
async (req: Request, res: Response) => {
// TODO: pull quests from config
console.debug("GET /quests/@me is incomplete");
res.json({
excluded_quests: [],
quest_enrollment_blocked_until: null,
quests: [
{
id: "1333839522189938740",
config: {
id: "1333839522189938740",
config_version: 2,
starts_at: "2025-07-14T14:00:00+00:00",
expires_at: "2099-08-15T23:00:00+00:00",
features: [
QuestFeature.QUEST_BAR_V2,
QuestFeature.REWARD_HIGHLIGHTING,
QuestFeature.DISMISSAL_SURVEY,
QuestFeature.QUESTS_CDN,
QuestFeature.PACING_CONTROLLER,
],
application: {
link: "https://spacebar.chat",
id: "545364944258990091",
name: "Spacebar",
},
colors: {
primary: "#5865F2",
secondary: "#000000",
},
assets: {
hero: "quests/1333839522189938740/orbs_quest_card_banner_4.jpeg",
hero_video: null,
quest_bar_hero: "quests/1333839522189938740/orbs_quest_bar.png",
quest_bar_hero_video: null,
game_tile: "discord_game_tile.png",
logotype: "discord_logo.png",
game_tile_light: "quests/1333839522189938740/1417603112168067182.png",
game_tile_dark: "quests/1333839522189938740/1417603112742551603.png",
logotype_light: "quests/1333839522189938740/1417603113304719540.png",
logotype_dark: "quests/1333839522189938740/1417603113791131668.png",
},
messages: {
quest_name: "Spacebar Bars Intro",
game_title: "Spacebar",
game_publisher: "Spacebar",
},
task_config_v2: {
tasks: {
[QuestEventType.WATCH_VIDEO]: {
type: QuestEventType.WATCH_VIDEO,
target: 31,
assets: {
video: {
url: "quests/1410358070831480904/1420884840815005717_1080.mp4",
width: 1080,
height: 1920,
thumbnail: "quests/1410358070831480904/1421253196549984267.png",
caption: "quests/1410358070831480904/1410370389451866112.vtt",
transcript: "quests/1410358070831480904/1410370413032374293.txt",
},
video_low_res: {
url: "quests/1410358070831480904/1420884840815005717_720.mp4",
width: 1080,
height: 1920,
thumbnail: "quests/1410358070831480904/1421253196549984267.png",
caption: "quests/1410358070831480904/1410370389451866112.vtt",
transcript: "quests/1410358070831480904/1410370413032374293.txt",
},
video_hls: {
url: "quests/1410358070831480904/1420884840815005717.m3u8",
width: 1080,
height: 1920,
thumbnail: "quests/1410358070831480904/1421253196549984267.png",
caption: "quests/1410358070831480904/1410370389451866112.vtt",
transcript: "quests/1410358070831480904/1410370413032374293.txt",
},
},
messages: {
video_title: "Intro to Spacebar Bars",
// video_end_cta_title: "Learn more about Bars",
// video_end_cta_subtitle: "Head to our Help Center for more information.",
},
},
},
join_operator: "or",
},
rewards_config: {
assignment_method: QuestAssignmentMethod.ALL,
rewards: [
{
type: QuestRewardType.VIRTUAL_CURRENCY,
sku_id: "1287881739531976815",
messages: {
name: "150 Bars",
name_with_article: "150 Bars",
redemption_instructions_by_platform: {
[QuestPlatformType.CROSS_PLATFORM]: "Default",
},
},
orb_quantity: 150,
},
],
rewards_expire_at: "2099-02-01T00:00:27+00:00",
platforms: [QuestPlatformType.CROSS_PLATFORM],
},
share_policy: QuestSharePolicy.SHAREABLE_EVERYWHERE,
cta_config: {
link: "https://spacebar.chat",
button_label: "Learn More",
subtitle: "Head to our Help Center for more information.",
},
},
// user_status: {
// user_id: "498989696412549120",
// quest_id: "1333839522189938740",
// enrolled_at: "2025-07-14T21:20:04.640772+00:00",
// completed_at: "2025-07-14T21:21:05.941315+00:00",
// claimed_at: "2025-07-14T21:21:26.983570+00:00",
// claimed_tier: null,
// last_stream_heartbeat_at: null,
// stream_progress_seconds: 0,
// dismissed_quest_content: 0,
// progress: {
// WATCH_VIDEO: {
// value: 31,
// event_name: "WATCH_VIDEO",
// updated_at: "2025-07-14T21:21:05.941317+00:00",
// completed_at: "2025-07-14T21:21:05.941315+00:00",
// heartbeat: null,
// },
// },
// },
user_status: null,
},
{
id: "1410358070831480904",
config: {
id: "1410358070831480904",
config_version: 2,
starts_at: "2025-11-17T16:30:27+00:00",
expires_at: "2099-01-02T00:00:27+00:00",
features: [
QuestFeature.QUEST_BAR_V2,
QuestFeature.REWARD_HIGHLIGHTING,
QuestFeature.DISMISSAL_SURVEY,
QuestFeature.MOBILE_QUEST_DOCK,
QuestFeature.QUESTS_CDN,
QuestFeature.PACING_CONTROLLER,
QuestFeature.VIDEO_QUEST_FORCE_HLS_VIDEO,
QuestFeature.VIDEO_QUEST_FORCE_END_CARD_CTA_SWAP,
QuestFeature.MOBILE_ONLY_QUEST_PUSH_TO_MOBILE,
],
application: {
link: "https://spacebar.chat",
id: "545364944258990091",
name: "Spacebar",
},
assets: {
hero: "quests/1410358070831480904/1440427094801911919.jpg",
hero_video: null,
quest_bar_hero: "quests/1410358070831480904/1440368088196579358.jpg",
quest_bar_hero_video: null,
game_tile: "687100d5-5958-43aa-b221-dfe4f2b79e14.png",
logotype: "0c58c77e-4ab4-482e-b784-ea4646b992ca.png",
game_tile_light: "quests/1410358070831480904/1417602409018163210.png",
game_tile_dark: "quests/1410358070831480904/1417602409668153406.png",
logotype_light: "quests/1410358070831480904/1417602410142105680.png",
logotype_dark: "quests/1410358070831480904/1417602410733375498.png",
},
colors: {
primary: "#4752C4",
secondary: "#000000",
},
messages: {
quest_name: "Mobile Bars Intro",
game_title: "Earn from Quests. Spend in Shop. Reward Your Play.",
game_publisher: "Spacebar",
},
// task_config: {
// type: 1,
// join_operator: "or",
// tasks: {
// WATCH_VIDEO_ON_MOBILE: {
// event_name: "WATCH_VIDEO_ON_MOBILE",
// target: 31,
// external_ids: [],
// },
// },
// },
task_config_v2: {
tasks: {
[QuestEventType.WATCH_VIDEO_ON_MOBILE]: {
type: QuestEventType.WATCH_VIDEO_ON_MOBILE,
target: 31,
assets: {
video: {
url: "quests/1410358070831480904/1420884840815005717_1080.mp4",
width: 1080,
height: 1920,
thumbnail: "quests/1410358070831480904/1421253196549984267.png",
caption: "quests/1410358070831480904/1410370389451866112.vtt",
transcript: "quests/1410358070831480904/1410370413032374293.txt",
},
video_low_res: {
url: "quests/1410358070831480904/1420884840815005717_720.mp4",
width: 1080,
height: 1920,
thumbnail: "quests/1410358070831480904/1421253196549984267.png",
caption: "quests/1410358070831480904/1410370389451866112.vtt",
transcript: "quests/1410358070831480904/1410370413032374293.txt",
},
video_hls: {
url: "quests/1410358070831480904/1420884840815005717.m3u8",
width: 1080,
height: 1920,
thumbnail: "quests/1410358070831480904/1421253196549984267.png",
caption: "quests/1410358070831480904/1410370389451866112.vtt",
transcript: "quests/1410358070831480904/1410370413032374293.txt",
},
},
messages: {
video_title: "Intro to Spacebar Bars",
// video_end_cta_title: "Learn more about Bars",
// video_end_cta_subtitle: "Head to our Help Center for more information.",
},
},
},
join_operator: "or",
},
rewards_config: {
assignment_method: QuestAssignmentMethod.ALL,
rewards: [
{
type: QuestRewardType.VIRTUAL_CURRENCY,
sku_id: "1287881739531976815",
messages: {
name: "150 Bars",
name_with_article: "150 Bars",
redemption_instructions_by_platform: {
[QuestPlatformType.CROSS_PLATFORM]: "Default",
},
},
orb_quantity: 150,
},
],
rewards_expire_at: "2099-02-01T00:00:27+00:00",
platforms: [QuestPlatformType.CROSS_PLATFORM],
},
share_policy: QuestSharePolicy.SHAREABLE_EVERYWHERE,
cta_config: {
link: "https://spacebar.chat",
button_label: "Learn More",
subtitle: "Head to our Help Center for more information.",
},
},
// user_status: {
// user_id: "498989696412549120",
// quest_id: "1410358070831480904",
// enrolled_at: "2025-11-19T04:17:07.950593+00:00",
// completed_at: "2025-11-19T04:17:45.920547+00:00",
// claimed_at: "2025-11-19T04:17:51.972621+00:00",
// claimed_tier: null,
// last_stream_heartbeat_at: null,
// stream_progress_seconds: 0,
// dismissed_quest_content: 0,
// progress: {
// WATCH_VIDEO_ON_MOBILE: {
// value: 31,
// event_name: "WATCH_VIDEO_ON_MOBILE",
// updated_at: "2025-11-19T04:17:45.920548+00:00",
// completed_at: "2025-11-19T04:17:45.920547+00:00",
// heartbeat: null,
// },
// },
// },
user_status: null,
targeted_content: [],
preview: false,
},
],
} as QuestsResponseSchema);
},
);
router.get(
"/claimed",
route({
description: "Returns information on the claimed quests for the current user.",
responses: {
200: {
body: "ClaimedQuestsResponseSchema",
},
},
}),
async (req: Request, res: Response) => {
// TODO: implement
res.json({
quests: [],
} as ClaimedQuestsResponseSchema);
},
);
export default router;

View File

@@ -0,0 +1,63 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import { QuestPlacementArea, QuestPlacementResponseSchema } from "@spacebar/schemas";
import { FieldErrors } from "@spacebar/util";
import { Request, Response, Router } from "express";
const router = Router({ mergeParams: true });
router.get(
"/",
route({
description: "Returns the sponsored quest that should be shown to the user in a specific placement.",
query: {
placement: {
type: "number",
description: "The quest placement area to get the quest for",
},
client_heartbeat_session_id: {
type: "string",
description: "A client-generated UUID representing the current persisted analytics heartbeat",
},
},
responses: {
200: {
body: "QuestPlacementResponseSchema",
},
},
}),
async (req: Request, res: Response) => {
const { placement, client_heartbeat_session_id } = req.query;
// check if placement is a valid QuestPlacementArea
if (!Object.values(QuestPlacementArea).includes(placement as unknown as number)) {
throw FieldErrors({
placement: {
code: "ENUM_TYPE_COERCE",
message: req.t("common:field.ENUM_TYPE_COERCE", {
value: placement,
}),
},
});
}
res.json({} as QuestPlacementResponseSchema);
},
);
export default router;

View File

@@ -0,0 +1,39 @@
/*
Spacebar: A FOSS re-implementation and extension of the Discord.com backend.
Copyright (C) 2023 Spacebar and Spacebar Contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { route } from "@spacebar/api";
import { Request, Response, Router } from "express";
const router = Router({ mergeParams: true });
router.get(
"/",
route({
responses: {
200: {
body: "VirtualCurrencyResponseSchema",
},
},
}),
async (req: Request, res: Response) => {
res.json({
balance: req.user.currency,
});
},
);
export default router;