diff --git a/.github/GOVERNANCE.md b/.github/GOVERNANCE.md
index c28a2744a..f16128de0 100644
--- a/.github/GOVERNANCE.md
+++ b/.github/GOVERNANCE.md
@@ -16,4 +16,10 @@ Every commit made by a contributor must include the [Developer Certificate of Or
## Name and marks
-The AGPL does not grant permission to use the Fluxer name, logo or other branding. Forks must use a distinct name and branding unless Fluxer Platform AB grants permission otherwise.
+Fluxer and the Fluxer logo are trademarks of Fluxer Platform AB. Neither the AGPL nor the CC BY-SA 4.0 licence on Fluxer artwork grants trademark rights. Fluxer Platform AB grants everyone the following permissions.
+
+- You may distribute unmodified builds of Fluxer, or builds with light patches, under the Fluxer name and logo. Light patches are changes for packaging, portability, security and bug fixes, configuration defaults and translations. Linux distributions, nixpkgs, Flathub and container images are all covered.
+- A self-hosted instance running such a build may show the Fluxer name and logo under the instance's own name and domain, as long as it does not imply affiliation with or endorsement by Fluxer Platform AB.
+- You may refer to Fluxer by name to describe compatibility, for example "works with Fluxer".
+
+Forks with substantive functional changes must use their own name and logo. Any other use needs permission from Fluxer Platform AB. Contact support@fluxer.com.
diff --git a/README.md b/README.md
index 0a6743ee9..8dae3c930 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@
Fluxer is a free and open source instant messaging and VoIP chat app built for friends, groups, and communities.
-
+
## Download
@@ -143,14 +143,13 @@ Full setup notes, including canary, are in the [Linux repositories documentation
The source is licensed under the [AGPL-3.0-or-later](./LICENSE) license.
-Fluxer branding, icons, default avatars, badge artwork, screenshots and marketing
-imagery are copyright Fluxer, all rights reserved, as set out in
-[fluxer_static/LICENSE](./fluxer_static/LICENSE). Third-party material keeps its own
-terms, listed in
+Fluxer artwork, such as the logo, icons, badges and default avatars, is
+licensed under [CC BY-SA 4.0](./fluxer_static/LICENSE). Third-party material
+keeps its own terms, listed in
[fluxer_static/THIRD_PARTY_LICENSES.md](./fluxer_static/THIRD_PARTY_LICENSES.md).
-Public availability of this repository does not grant trademark, brand, or
-endorsement rights.
+Use of the Fluxer name and logo is covered by the
+[name and marks policy](./.github/GOVERNANCE.md#name-and-marks).
[win-setup-x64]: https://pkgs.fluxer.com/desktop/stable/win32/x64/latest/setup
[win-setup-arm64]: https://pkgs.fluxer.com/desktop/stable/win32/arm64/latest/setup
diff --git a/deploy/self-hosting/.env.example b/deploy/self-hosting/.env.example
index 9c4eabe11..b657fd7a2 100644
--- a/deploy/self-hosting/.env.example
+++ b/deploy/self-hosting/.env.example
@@ -357,9 +357,8 @@ FLUXER_DISCOVERY_ENABLED=true
#FLUXER_GIFT_ENDPOINT=
#FLUXER_MEDIA_PROXY_UPLOAD_RELAY_ENDPOINT=
#PUBLIC_BOOTSTRAP_API_PUBLIC_ENDPOINT=
-# These follow FLUXER_STATIC_CDN_ENDPOINT first, then the public origin.
+# This follows FLUXER_STATIC_CDN_ENDPOINT first, then the public origin.
#FLUXER_GATEWAY_STATIC_CDN_ENDPOINT=
-#FLUXER_UNFURL_STATIC_CDN_ENDPOINT=
# These follow FLUXER_MEDIA_ENDPOINT first, then the public origin.
#FLUXER_MEDIA_PROXY_PUBLIC_ENDPOINT=
#FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT=
diff --git a/deploy/self-hosting/Caddyfile b/deploy/self-hosting/Caddyfile
index b0dcbf2a6..28657aab5 100644
--- a/deploy/self-hosting/Caddyfile
+++ b/deploy/self-hosting/Caddyfile
@@ -42,7 +42,7 @@
reverse_proxy admin:8080
}
- @staticAssets path /web/* /emoji/* /libs/* /avatars/* /badges/* /desktop/* /embeds/*
+ @staticAssets path /web/* /emoji/* /libs/* /avatars/* /badges/* /desktop/*
handle @staticAssets {
reverse_proxy static-proxy:8080
}
diff --git a/deploy/self-hosting/docker-compose.yml b/deploy/self-hosting/docker-compose.yml
index c573bc1d7..a83021fa1 100644
--- a/deploy/self-hosting/docker-compose.yml
+++ b/deploy/self-hosting/docker-compose.yml
@@ -845,8 +845,6 @@ services:
FLUXER_SVC_MODE: shard
FLUXER_SVC_SHARD_ID: "0"
FLUXER_MEDIA_PROXY_ENDPOINT: http://media-proxy:8080
- FLUXER_UNFURL_STATIC_CDN_ENDPOINT: ${FLUXER_UNFURL_STATIC_CDN_ENDPOINT:-}
- FLUXER_STATIC_CDN_ENDPOINT: ${FLUXER_STATIC_CDN_ENDPOINT:-${FLUXER_PUBLIC_ORIGIN:-${FLUXER_PUBLIC_SCHEME:-https}://${FLUXER_DOMAIN}}}
healthcheck: *fluxer-svc-healthcheck
depends_on:
nats: {condition: service_healthy}
diff --git a/fluxer_api/src/api/assets/github.webp b/fluxer_api/src/api/assets/github.webp
deleted file mode 100644
index fbf3c79b9..000000000
Binary files a/fluxer_api/src/api/assets/github.webp and /dev/null differ
diff --git a/fluxer_api/src/api/assets/instatus.webp b/fluxer_api/src/api/assets/instatus.webp
deleted file mode 100644
index a9704c116..000000000
Binary files a/fluxer_api/src/api/assets/instatus.webp and /dev/null differ
diff --git a/fluxer_api/src/api/channel/tests/AttachmentDecay.test.ts b/fluxer_api/src/api/channel/tests/AttachmentDecay.test.ts
index c11d3005c..58617bc19 100644
--- a/fluxer_api/src/api/channel/tests/AttachmentDecay.test.ts
+++ b/fluxer_api/src/api/channel/tests/AttachmentDecay.test.ts
@@ -76,7 +76,7 @@ describe('Attachment Decay', () => {
const channel = await createChannel(harness, account.token, guild.id, 'test-channel');
const channelId = guild.system_channel_id ?? channel.id;
const file1Data = loadFixture('yeah.png');
- const file2Data = loadFixture('thisisfine.gif');
+ const file2Data = loadFixture('animated.gif');
const {response, json} = await sendMessageWithAttachments(
harness,
account.token,
@@ -258,7 +258,7 @@ describe('Attachment Decay', () => {
const channel = await createChannel(harness, account.token, guild.id, 'test-channel');
const channelId = guild.system_channel_id ?? channel.id;
const smallFile = loadFixture('yeah.png');
- const largeFile = loadFixture('thisisfine.gif');
+ const largeFile = loadFixture('animated.gif');
const smallResult = await sendMessageWithAttachments(
harness,
account.token,
diff --git a/fluxer_api/src/api/channel/tests/AttachmentUploadValidation.test.ts b/fluxer_api/src/api/channel/tests/AttachmentUploadValidation.test.ts
index 0346638f2..296dd3149 100644
--- a/fluxer_api/src/api/channel/tests/AttachmentUploadValidation.test.ts
+++ b/fluxer_api/src/api/channel/tests/AttachmentUploadValidation.test.ts
@@ -327,17 +327,17 @@ describe('Attachment Upload Validation', () => {
const channel = await createChannel(harness, account.token, guild.id, 'test-channel');
const channelId = guild.system_channel_id ?? channel.id;
const file1Data = loadFixture('yeah.png');
- const file2Data = loadFixture('thisisfine.gif');
+ const file2Data = loadFixture('animated.gif');
const payload = {
content: 'Ordered files test',
attachments: [
{id: 0, filename: 'yeah.png', description: 'First file', title: 'First'},
- {id: 1, filename: 'thisisfine.gif', description: 'Second file', title: 'Second'},
+ {id: 1, filename: 'animated.gif', description: 'Second file', title: 'Second'},
],
};
const {response, json} = await sendMessageWithAttachments(harness, account.token, channelId, payload, [
{index: 0, filename: 'yeah.png', data: file1Data},
- {index: 1, filename: 'thisisfine.gif', data: file2Data},
+ {index: 1, filename: 'animated.gif', data: file2Data},
]);
expect(response.status).toBe(200);
expect(json.attachments).toBeDefined();
@@ -346,7 +346,7 @@ describe('Attachment Upload Validation', () => {
expect(json.attachments![0].filename).toBe('yeah.png');
expect(json.attachments![0].description).toBe('First file');
expect(json.attachments![0].title).toBe('First');
- expect(json.attachments![1].filename).toBe('thisisfine.gif');
+ expect(json.attachments![1].filename).toBe('animated.gif');
expect(json.attachments![1].description).toBe('Second file');
expect(json.attachments![1].title).toBe('Second');
});
@@ -356,17 +356,17 @@ describe('Attachment Upload Validation', () => {
const channel = await createChannel(harness, account.token, guild.id, 'test-channel');
const channelId = guild.system_channel_id ?? channel.id;
const file1Data = loadFixture('yeah.png');
- const file2Data = loadFixture('thisisfine.gif');
+ const file2Data = loadFixture('animated.gif');
const payload = {
content: 'Sparse IDs test',
attachments: [
{id: 2, filename: 'yeah.png', description: 'ID is 2', title: 'Two'},
- {id: 5, filename: 'thisisfine.gif', description: 'ID is 5', title: 'Five'},
+ {id: 5, filename: 'animated.gif', description: 'ID is 5', title: 'Five'},
],
};
const {response, json} = await sendMessageWithAttachments(harness, account.token, channelId, payload, [
{index: 2, filename: 'yeah.png', data: file1Data},
- {index: 5, filename: 'thisisfine.gif', data: file2Data},
+ {index: 5, filename: 'animated.gif', data: file2Data},
]);
expect(response.status).toBe(200);
expect(json.attachments).toBeDefined();
@@ -472,7 +472,7 @@ describe('Attachment Upload Validation', () => {
const channel = await createChannel(harness, account.token, guild.id, 'test-channel');
const channelId = guild.system_channel_id ?? channel.id;
const file1Data = loadFixture('yeah.png');
- const file2Data = loadFixture('thisisfine.gif');
+ const file2Data = loadFixture('animated.gif');
const payload = {
content: 'Mixed metadata test',
attachments: [
@@ -485,13 +485,13 @@ describe('Attachment Upload Validation', () => {
},
{
id: 1,
- filename: 'thisisfine.gif',
+ filename: 'animated.gif',
},
],
};
const {response, json} = await sendMessageWithAttachments(harness, account.token, channelId, payload, [
{index: 0, filename: 'yeah.png', data: file1Data},
- {index: 1, filename: 'thisisfine.gif', data: file2Data},
+ {index: 1, filename: 'animated.gif', data: file2Data},
]);
expect(response.status).toBe(200);
expect(json.attachments).toBeDefined();
diff --git a/fluxer_api/src/api/channel/tests/CrosspostWorkerTestUtils.ts b/fluxer_api/src/api/channel/tests/CrosspostWorkerTestUtils.ts
index 0bb5a77b3..7d580613b 100644
--- a/fluxer_api/src/api/channel/tests/CrosspostWorkerTestUtils.ts
+++ b/fluxer_api/src/api/channel/tests/CrosspostWorkerTestUtils.ts
@@ -76,7 +76,7 @@ export async function sendWithImage(
filenames.map((filename, index) => ({
index,
filename,
- data: loadFixture(filename.endsWith('.gif') ? 'thisisfine.gif' : 'yeah.png'),
+ data: loadFixture(filename.endsWith('.gif') ? 'animated.gif' : 'yeah.png'),
})),
);
if (response.status !== 200) {
diff --git a/fluxer_api/src/api/channel/tests/EmbedAttachmentUrlResolution.test.ts b/fluxer_api/src/api/channel/tests/EmbedAttachmentUrlResolution.test.ts
index 680ac046d..62c9e5418 100644
--- a/fluxer_api/src/api/channel/tests/EmbedAttachmentUrlResolution.test.ts
+++ b/fluxer_api/src/api/channel/tests/EmbedAttachmentUrlResolution.test.ts
@@ -86,25 +86,25 @@ describe('Embed Attachment URL Resolution', () => {
const channel = await createChannel(harness, account.token, guild.id, 'test-channel');
const channelId = guild.system_channel_id ?? channel.id;
const file1Data = loadFixture('yeah.png');
- const file2Data = loadFixture('thisisfine.gif');
+ const file2Data = loadFixture('animated.gif');
const payload = {
content: 'Both image and thumbnail',
attachments: [
{id: 0, filename: 'yeah.png'},
- {id: 1, filename: 'thisisfine.gif'},
+ {id: 1, filename: 'animated.gif'},
],
embeds: [
{
title: 'Complete Embed',
description: 'This embed uses both image and thumbnail',
- image: {url: 'attachment://thisisfine.gif'},
+ image: {url: 'attachment://animated.gif'},
thumbnail: {url: 'attachment://yeah.png'},
},
],
};
const {response, json} = await sendMessageWithAttachments(harness, account.token, channelId, payload, [
{index: 0, filename: 'yeah.png', data: file1Data},
- {index: 1, filename: 'thisisfine.gif', data: file2Data},
+ {index: 1, filename: 'animated.gif', data: file2Data},
]);
expect(response.status).toBe(200);
expect(json.embeds).toBeDefined();
@@ -576,12 +576,12 @@ describe('Embed Attachment URL Resolution', () => {
const channel = await createChannel(harness, account.token, guild.id, 'test-channel');
const channelId = guild.system_channel_id ?? channel.id;
const file1Data = loadFixture('yeah.png');
- const file2Data = loadFixture('thisisfine.gif');
+ const file2Data = loadFixture('animated.gif');
const payload = {
content: 'Multiple embeds with different attachments',
attachments: [
{id: 0, filename: 'yeah.png'},
- {id: 1, filename: 'thisisfine.gif'},
+ {id: 1, filename: 'animated.gif'},
],
embeds: [
{
@@ -592,19 +592,19 @@ describe('Embed Attachment URL Resolution', () => {
{
title: 'Second Embed',
description: 'Uses GIF',
- image: {url: 'attachment://thisisfine.gif'},
+ image: {url: 'attachment://animated.gif'},
},
],
};
const {response, json} = await sendMessageWithAttachments(harness, account.token, channelId, payload, [
{index: 0, filename: 'yeah.png', data: file1Data},
- {index: 1, filename: 'thisisfine.gif', data: file2Data},
+ {index: 1, filename: 'animated.gif', data: file2Data},
]);
expect(response.status).toBe(200);
expect(json.embeds).toBeDefined();
expect(json.embeds).toHaveLength(2);
expect(json.embeds![0].image?.url).toContain('yeah.png');
- expect(json.embeds![1].image?.url).toContain('thisisfine.gif');
+ expect(json.embeds![1].image?.url).toContain('animated.gif');
});
it('should resolve multiple files referenced by embeds', async () => {
const account = await createTestAccount(harness);
diff --git a/fluxer_api/src/api/test/fixtures/animated.gif b/fluxer_api/src/api/test/fixtures/animated.gif
new file mode 100644
index 000000000..048f43348
Binary files /dev/null and b/fluxer_api/src/api/test/fixtures/animated.gif differ
diff --git a/fluxer_api/src/api/test/fixtures/ncmec/xsd/report.xsd b/fluxer_api/src/api/test/fixtures/ncmec/xsd/report.xsd
deleted file mode 100644
index 0496ea312..000000000
--- a/fluxer_api/src/api/test/fixtures/ncmec/xsd/report.xsd
+++ /dev/null
@@ -1,3154 +0,0 @@
-
-
-
-
- This document describes the ESP incident reporting format.
-
-
-
- This is the main top level tag to define an incident report.
-
-
-
-
-
- Report represents a batch of otherwise individual reports that have been grouped for the specified reason.
-
-
-
-
- This contains general incident information.
-
-
-
-
- This record summarizes the details of the incident being reported. There can be more than one for different incident details, but there must be at least of these for each report.
-
-
-
-
- The law enforcement contact information related to the event. This is used if the incident information has already been reported to law enforcement or if the request originated from law enforcement.
-
-
-
-
- This is information related to the person or organization reporting the incident.
-
-
-
-
- There can be 0 or 1 reported user or person involved in the incident.
-
-
-
-
- There can be 0 or more intended recipients involved in the incident.
-
-
-
-
- There can be 0 or more victim records for each incident.
-
-
-
-
- Any additional notes on this incident not covered by any other section.
-
-
-
-
-
-
-
-
-
-
-
- This field describes the type of incident being reported.
-
-
-
-
- The full application name where the reported content occurred.
-
-
-
-
- Company indicated a higher level of urgency (immediate risk to child) and the reason for this escalation. Only enter text if you want to escalate the report.
-
-
-
-
- Tags indicated by ESP to describe the reported incident.
-
-
-
-
- The date/time of the reported incident.
-
-
-
-
- This is a text field that allows you to enter any additional information related to the incidentDateTime.
-
-
-
-
-
-
-
-
-
-
- Is the report associated with sextortion?
-
-
-
-
- Is the report associated with solicitation for child sexual assault material?
-
-
-
-
- Is the report associated with an interaction between minors?
-
-
-
-
- Is the report associated with spam?
-
-
-
-
- Is the report associated with sadistic online exploitation?
-
-
-
-
- The report is associated with an incident that is informational in nature. Supplying a value will de-escalate the report.
-
-
-
-
-
-
-
- This record summarizes the details of the incident being reported. There can be one or more for different incident details. Each record must have one of the following subtypes.
-
-
-
-
-
- The incident occurred on a web page.
-
-
-
-
- The incident occurred over Email.
-
-
-
-
- The incident occurred in a newsgroup.
-
-
-
-
- The incident occurred over IM or during a chat session.
-
-
-
-
- The incident occurred during an online game.
-
-
-
-
- This category is used when the primary method of communication between the child and/or reported person or user is through a cell phone. This can include phone conversations, text messages, or images sent through the phone.
-
-
-
-
- This category of the incident does not involve an online or computer component or a cell phone (e.g., face-to-face contact or potentially illegal hard copy or printed materials).
-
-
-
-
- The incident occurred via a Peer to Peer (also known as P2P) decentralized network that allows users to share files through a direct connection.
-
-
-
-
-
-
-
-
- The incident occurred on a web page.
-
-
-
-
-
- This is the actual URL where the reported incident occurred. There can be as many of these as needed.
-
-
-
-
- This is a text field that allows you to enter any additional information related to the web page event.
-
-
-
-
-
- True if the reported URL is hosted on another company's server. The reporting company has no further information on this incident and is providing it to NCMEC for informational purposes.
-
-
-
-
-
-
-
- The incident occurred over email.
-
-
-
-
-
- An email address associated with the incident.
-
-
-
-
- The headers, subject, and content of the communication.
-
-
-
-
- Any additional notes to add to the email event.
-
-
-
-
-
-
-
-
-
- The incident occurred in a newsgroup.
-
-
-
-
- The newsgroup name.
-
-
-
-
- An email address associated with the incident.
-
-
-
-
- The content of the newsgroup posting including any headers, subject.
-
-
-
-
- Any additional notes to add to the newsgroup event.
-
-
-
-
-
-
-
-
- The incident occurred over IM or during a chat session.
-
-
-
-
-
- The chat or instant messenger program used.
-
-
-
-
- The name of the chat room.
-
-
-
-
- The content of the chat, any chat logs.
-
-
-
-
- Any additional information related to the chat/instant messaging incident.
-
-
-
-
-
-
-
-
- The incident occurred during an online game.
-
-
-
-
-
- The name of the game the incident occurred in.
-
-
-
-
- The name of the gaming console if one was used.
-
-
-
-
- The content of the message/chat that occurred during the game.
-
-
-
-
- Any additional information about the online gaming incident.
-
-
-
-
-
-
-
-
- This category is used when the primary method of communication between the child and/or reported person or user is through a cell phone. This can include phone conversations, text messages, or images sent through the phone.
-
-
-
-
-
- The cell phone number associated with the incident.
-
-
-
-
- The latitude associated with the incident.
-
-
-
-
- The longitude associated with the incident.
-
-
-
-
- Any additional information about the cell phone incident.
-
-
-
-
-
-
-
-
- This category of the incident does not involve an online or computer component or a cell phone (e.g., face-to-face contact or potentially illegal hard copy or printed materials).
-
-
-
-
-
- The location associated with the incident.
-
-
-
-
- Incident's address.
-
-
-
-
- Please describe in detail the incident being reported.
-
-
-
-
-
-
-
-
- The incident occurred via a Peer to Peer (also known as P2P) decentralized network that allows users to share files through a direct connection.
-
-
-
-
-
- The Peer to Peer Client used (e.g., Phex, Emule).
-
-
-
-
- An IP address used by the person or user reported.
-
-
-
-
- The name of file(s).
-
-
-
-
- Any additional information about the cell phone incident.
-
-
-
-
-
-
-
-
- The law enforcement contact information related to the event. This is used if the incident information has already been reported to law enforcement or if the request originated from law enforcement.
-
-
-
-
-
- The law enforcement agency name involved.
-
-
-
-
- The case number if known.
-
-
-
-
- The contact information for the investigator or officer on the case.
-
-
-
-
- Set this to true if the incident was reported to law enforcement by the company.
-
-
-
-
- Set this to true if domestic law enforcement has served legal process about this person/user or reported incident.
-
-
-
-
- Set this to true if international law enforcement has served legal process about this person/user or reported incident.
-
-
-
-
-
-
- The name of the country associated with this report from which legal process was received.
-
-
-
-
-
-
-
-
-
-
-
-
- This is information related to the person or company reporting the incident.
-
-
-
-
-
- Details of the person reporting the information and the company's address.
-
-
-
-
- Use this field if a person other than the person submitting the report should be contacted by law enforcement regarding the report.
-
-
-
-
- This open-text field allows a company to include language (not specific to the reported incident) that they want included in every CyberTipline report. For instance, basic company information, how legal process should be served, length of data retention, 24-hour emergency phone numbers, etc..
-
-
-
-
- Include Terms of Service (TOS) relevant to the incident being reported.
-
-
-
-
- Company can include a URL if they wish to direct attention to a web page for further information.
-
-
-
-
-
-
-
-
-
-
-
- The person information about the reported person or user in the incident.
-
-
-
-
- The description for the vehicle of the reported person.
-
-
-
-
- The unique ID of the reported person or user in the ESP's system (e.g., friend ID).
-
-
-
-
- The name of the reporter's product or service that was used by the reported person or user during the incident.
-
-
-
-
- This account has been compromised, hacked, or hijacked.
-
-
-
-
- The screen name of the reported person or user.
-
-
-
-
- A display name, other than a screen name or a username, for the reported person or user.
-
-
-
-
- The different identifying URLs for this reported person or user.
-
-
-
-
- A copy of the user's bio as per their account according to the reporting company's platform.
-
-
-
-
- An originating IP address used by the reported person or user and when it was used.
-
-
-
-
- An ID for a device used by the reported person or user and, optionally, when it was used.
-
-
-
-
- Reported person or user is using another company's service. The reporting company has no further information about this person or user.
-
-
-
-
- List report ID numbers for any prior CyberTipline reports on this reported person or user.
-
-
-
-
- The ESP believes the reported person or user is engaged in an organized online group related to child sexual exploitation. This open text field is used to provide unique group identifiers (e.g., group name, group ID, etc.).
-
-
-
-
- Whether the account was temporarily disabled at any point.
-
-
-
-
- Whether the account was permanently disabled.
-
-
-
-
- The reporter's estimated location for the reported person or user.
-
-
-
-
- There are no additional emails to report.
-
-
-
-
- Any additional account associated with this reported person or user.
-
-
-
-
- Any additional notes on this reported person or user not covered by any other section
-
-
-
-
-
-
-
-
-
-
-
- The person information about the intended recipient.
-
-
-
-
- The unique ID of the intended recipient in the ESP's system (e.g., friend ID).
-
-
-
-
- The name of the reporter's product or service that was used by the intended recipient during the incident.
-
-
-
-
- This account has been compromised, hacked, or hijacked.
-
-
-
-
- The screen name of the intended recipient.
-
-
-
-
- A display name, other than a screen name or a username, for the intended recipient.
-
-
-
-
- An identifying URL for this intended recipient.
-
-
-
-
- A copy of the user's bio as per their account according to the reporting company's platform.
-
-
-
-
- An IP address used by the intended recipient and when it was used.
-
-
-
-
- An ID for a device used by the intended recipient and when it was used.
-
-
-
-
- A CyberTipline report ID for any prior CyberTipline report on this intended recipient.
-
-
-
-
- The ESP believes the intended recipient is engaged in an organized online group related to child sexual exploitation. This open text field is used to provide unique group identifiers (e.g., group name, group ID, etc.).
-
-
-
-
- Whether the account was temporarily disabled at any point.
-
-
-
-
- Whether the account was permanently disabled.
-
-
-
-
- The reporter's estimated location for the intended recipient.
-
-
-
-
- There are no additional emails to report.
-
-
-
-
- Additional information about this intended recipient not covered by any other section.
-
-
-
-
-
-
-
-
- There can be 0 or more victim records for each incident.
-
-
-
-
-
- The victim's personal details such as name, address, phone numbers.
-
-
-
-
- The unique ID of the victim in the ESP's system (e.g., friend ID).
-
-
-
-
- The name of the reporter's product or service that was used by the victim during the incident.
-
-
-
-
- This account has been compromised, hacked, or hijacked.
-
-
-
-
- The screen name of the victim.
-
-
-
-
- A display name, other than a screen name or a username, for the victim.
-
-
-
-
- The identifying URL(s) for the victim.
-
-
-
-
- A copy of the user's bio as per their account according to the reporting company's platform.
-
-
-
-
- An originating IP address used by the victim and when it was used.
-
-
-
-
- An ID for a device used by the victim and, optionally, when it was used.
-
-
-
-
- Victim's school name.
-
-
-
-
- List of report ID numbers for any prior CyberTipline reports on this victim.
-
-
-
-
- Whether the account was temporarily disabled at any point.
-
-
-
-
- Whether the account was permanently disabled.
-
-
-
-
- The reporter's estimated location for the victim.
-
-
-
-
- There are no additional emails to report.
-
-
-
-
- Any additional account associated with this victim.
-
-
-
-
- Any additional notes on this victim.
-
-
-
-
-
-
-
-
- The root element of the file details.
-
-
-
-
-
- The report this file is related to.
-
-
-
-
- The file this report is related to.
-
-
-
-
- The name of the file being reported to NCMEC by the company.
-
-
-
-
- Original filename associated with the file when it was uploaded to the company's servers by the reported user or person.
-
-
-
-
- The date and time the file was uploaded to the reporting ESP.
-
-
-
-
- The URL where reported file was originally located.
-
-
-
-
- Has your company viewed the file being reported to NCMEC?
-
-
-
-
- Has your company viewed the EXIF for the file being reported to NCMEC?
-
-
-
-
- Was the reported file publicly accessible to online users?
-
-
-
-
- The relevance or relation of the uploaded file to the report. Unless specified otherwise, a file is "Reported" by default.
-
-
-
-
-
- Is the file being shared/posted out of mimicry or other seemingly non-malicious intent?
-
-
-
-
- Tags indicated by ESP to describe the reported file.
-
-
-
-
-
- This field should be used only if your company has executed the MOU to participate in the Industry Hash Sharing Platform. Only populate with Industry-created classification scale (A1, A2, B1, or B2).
-
-
-
-
- The original binary hash value of the file at the time it was uploaded by the reported user or person (prior to any potential modification by your company). Include required attribute hashType, specifying the type of hash submitted, e.g., MD5, SHA1, etc.
-
-
-
-
- This represents the upload IP address of the file and when it occurred.
-
-
-
-
- An ID for a device used to upload the file and, optionally, when it was used.
-
-
-
-
-
- Additional information regarding the uploaded file your company wishes to report (e.g., "viral video").
-
-
-
-
-
-
-
-
- All the file meta data name value pairs.
-
-
-
-
-
- The list of meta data.
-
-
-
-
-
- The type of meta data being sent.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- A person record. Can be used to represent a reported person or user, victim, or reporting person.
-
-
-
-
- Person's first name.
-
-
-
-
- Person's last name.
-
-
-
-
- Phone number and type.
-
-
-
-
- Contact email address (type and if verified).
-
-
-
-
- Person's address.
-
-
-
-
- Person's age.
-
-
-
-
- The asserted age for the Person conflicts with the reporter's determination of the Person's age.
-
-
-
-
- Person's date of birth.
-
-
-
-
-
-
-
- A person record with information pertinent for contact.
-
-
-
-
- Person's first name.
-
-
-
-
- Person's last name.
-
-
-
-
- Phone number and type.
-
-
-
-
- Contact email address (type and if verified).
-
-
-
-
- Person's address.
-
-
-
-
-
-
-
-
-
- Street Address.
-
-
-
-
- City.
-
-
-
-
- Zip or postal code.
-
-
-
-
- US State or territory.
-
-
-
-
- If not in the United States, the state or province.
-
-
-
-
- Country name or two letter country code.
-
-
-
-
-
- The type of address.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The complex type phone contains the phone number and if available, the phone type.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- This is an enumeration of all supported incident types. Originally, we only supported child pornography, but we have expanded to support child sex trafficking, child sex tourism, sexual molestation, misleading domain names, misleading worlds or images, online enticement, or unsolicited obscene material to a child.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- This represents the origination IP address of an action and when it occurred.
-
-
-
-
- The IP address from which the incident occurred.
-
-
-
-
- Type of IP Address reported, e.g., Login, Registration, Upload, etc.
-
-
-
-
- The time the incident occurred and the IP address was recorded.
-
-
-
-
- If the ESP has reason to believe this IP address is a proxy, please indicate this here.
-
-
-
-
- The port number of the IP address.
-
-
-
-
-
-
-
- This represents the capture of a device ID.
-
-
-
-
- The type of device ID captured, e.g., IMEI, ICCID, SSID, etc..
-
-
-
-
- The value of the ID captured.
-
-
-
-
- Event during which the company recorded the device ID capture reported, e.g., Login, Registration, Upload, etc..
-
-
-
-
- The time the incident occurred and the device ID was recorded.
-
-
-
-
-
-
-
- This defines the type of URL. Types include login, registration, etc. The available options are enumerated in the schema.
-
-
-
-
-
-
-
-
-
-
-
-
-
- The latitude or longitude is specified as a decimal number. Plus values represent north and east, negative values represents south and west.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The full application name of the associated account.
-
-
-
-
-
-
-
-
- Associated account person's first name.
-
-
-
-
- Associated account person's middle name.
-
-
-
-
- Associated account person's last name.
-
-
-
-
- Associated account person's approximate age.
-
-
-
-
- The asserted age for the associated account person conflicts with the reporter's determination of the associated account person's age.
-
-
-
-
- Associated account person's date of birth.
-
-
-
-
- Contact phone number (type and if verified).
-
-
-
-
- Contact email address (type and if verified).
-
-
-
-
- There are no additional emails to report.
-
-
-
-
- Associated account's address.
-
-
-
-
- The name of the reporter's product or service that was used by the associated account during the incident.
-
-
-
-
- The unique ID of the associated account in the ESP's system (e.g., friend ID).
-
-
-
-
- The identifying URL(s) for the associated account.
-
-
-
-
- The screen name of the associated account.
-
-
-
-
- A display name, other than a screen name or a username, for the associated account.
-
-
-
-
- A copy of the user's bio as per their account according to the associated account's platform.
-
-
-
-
- The ESP believes the associated account is engaged in an organized online group related to child sexual exploitation. This open text field is used to provide unique group identifiers (e.g., group name, group ID, etc.).
-
-
-
-
- This account has been compromised, hacked, or hijacked.
-
-
-
-
- Whether the account was temporarily disabled at any point.
-
-
-
-
- Whether the account was permanently disabled.
-
-
-
-
- An originating IP address used by the associated account and when it was used.
-
-
-
-
- An ID for a device used by the associated account and, optionally, when it was used.
-
-
-
-
- List of report ID numbers for any prior CyberTipline reports on this associated account.
-
-
-
-
- Any additional notes on this associated account.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Is the file depicting anime, drawing, cartoon, virtual or hentai?
-
-
-
-
- Is the file being shared/posted out of mimicry or other seemingly non-malicious intent?
-
-
-
-
- Is the file circulating rapidly from one user to another?
-
-
-
-
- Does the file contain content that is believed to be self- produced?
-
-
-
-
- Does the file depict an intentional act of causing physical injury or trauma to a person?
-
-
-
-
- Does the file depict graphic violence, including but not limited to acts of brutality or detailed or vivid gruesomeness?
-
-
-
-
- Does the file involve an animal(s)?
-
-
-
-
- Does the file depicts content that was streamed live at the time it was uploaded?
-
-
-
-
- Does the file depict an infant?
-
-
-
-
- Does the file contain content that is believed to be Generative Artificial Intelligence?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- The complex type hash contains a hash and hash type.
-
-
-
-
-
-
-
-
-
-
-
-
- The name of the meta data variable.
-
-
-
-
- The value of the meta data variable.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- This type supports the code of countries specified in the list of ISO 3166 Country Codes.
-
-
-
-
-
- Anonymous Proxy
-
-
-
-
- Satellite Provider
-
-
-
-
- Other Country
-
-
-
-
- Andorra
-
-
-
-
- United Arab Emirates
-
-
-
-
- Afghanistan
-
-
-
-
- Antigua and Barbuda
-
-
-
-
- Anguilla
-
-
-
-
- Albania
-
-
-
-
- Armenia
-
-
-
-
- Netherlands Antilles
-
-
-
-
- Angola
-
-
-
-
- Asia/Pacific Region
-
-
-
-
- Antarctica
-
-
-
-
- Argentina
-
-
-
-
- American Samoa
-
-
-
-
- Austria
-
-
-
-
- Australia
-
-
-
-
- Aruba
-
-
-
-
- Aland Islands
-
-
-
-
- Azerbaijan
-
-
-
-
- Bosnia and Herzegovina
-
-
-
-
- Barbados
-
-
-
-
- Bangladesh
-
-
-
-
- Belgium
-
-
-
-
- Burkina Faso
-
-
-
-
- Bulgaria
-
-
-
-
- Bahrain
-
-
-
-
- Burundi
-
-
-
-
- Benin
-
-
-
-
- Saint Bartelemey
-
-
-
-
- Bermuda
-
-
-
-
- Brunei Darussalam
-
-
-
-
- Bolivia
-
-
-
-
- Bonaire, Sint Eustatius and Saba
-
-
-
-
- Brazil
-
-
-
-
- Bahamas
-
-
-
-
- Bhutan
-
-
-
-
- Bouvet Island
-
-
-
-
- Botswana
-
-
-
-
- Belarus
-
-
-
-
- Belize
-
-
-
-
- Canada
-
-
-
-
- Cocos (Keeling) Islands
-
-
-
-
- Congo, The Democratic Republic of the
-
-
-
-
- Central African Republic
-
-
-
-
- Congo
-
-
-
-
- Switzerland
-
-
-
-
- Cote d'Ivoire
-
-
-
-
- Cook Islands
-
-
-
-
- Chile
-
-
-
-
- Cameroon
-
-
-
-
- China
-
-
-
-
- Colombia
-
-
-
-
- Costa Rica
-
-
-
-
- Cuba
-
-
-
-
- Cape Verde
-
-
-
-
- Christmas Island
-
-
-
-
- Curacao
-
-
-
-
- Cyprus
-
-
-
-
- Czech Republic
-
-
-
-
- Germany
-
-
-
-
- Djibouti
-
-
-
-
- Denmark
-
-
-
-
- Dominica
-
-
-
-
- Dominican Republic
-
-
-
-
- Algeria
-
-
-
-
- Ecuador
-
-
-
-
- Estonia
-
-
-
-
- Egypt
-
-
-
-
- Western Sahara
-
-
-
-
- Eritrea
-
-
-
-
- Spain
-
-
-
-
- Ethiopia
-
-
-
-
- Europe
-
-
-
-
- Finland
-
-
-
-
- Fiji
-
-
-
-
- Falkland Islands (Malvinas)
-
-
-
-
- Micronesia, Federated States of
-
-
-
-
- Faroe Islands
-
-
-
-
- France
-
-
-
-
- France, Metropolitan
-
-
-
-
- Gabon
-
-
-
-
- United Kingdom
-
-
-
-
- Grenada
-
-
-
-
- Georgia
-
-
-
-
- French Guiana
-
-
-
-
- Guernsey
-
-
-
-
- Ghana
-
-
-
-
- Gibraltar
-
-
-
-
- Greenland
-
-
-
-
- Gambia
-
-
-
-
- Guinea
-
-
-
-
- Guadeloupe
-
-
-
-
- Equatorial Guinea
-
-
-
-
- Greece
-
-
-
-
- South Georgia and the South Sandwich Islands
-
-
-
-
- Guatemala
-
-
-
-
- Guam
-
-
-
-
- Guinea-Bissau
-
-
-
-
- Guyana
-
-
-
-
- Hong Kong
-
-
-
-
- Heard Island and McDonald Islands
-
-
-
-
- Honduras
-
-
-
-
- Croatia
-
-
-
-
- Haiti
-
-
-
-
- Hungary
-
-
-
-
- Indonesia
-
-
-
-
- Ireland
-
-
-
-
- Israel
-
-
-
-
- Isle of Man
-
-
-
-
- India
-
-
-
-
- British Indian Ocean Territory
-
-
-
-
- Iraq
-
-
-
-
- Iran, Islamic Republic of
-
-
-
-
- Iceland
-
-
-
-
- Italy
-
-
-
-
- Jersey
-
-
-
-
- Jamaica
-
-
-
-
- Jordan
-
-
-
-
- Japan
-
-
-
-
- Kenya
-
-
-
-
- Kyrgyzstan
-
-
-
-
- Cambodia
-
-
-
-
- Kiribati
-
-
-
-
- Comoros
-
-
-
-
- Saint Kitts and Nevis
-
-
-
-
- Korea, Democratic People's Republic of
-
-
-
-
- Korea, Republic of
-
-
-
-
- Kuwait
-
-
-
-
- Cayman Islands
-
-
-
-
- Kazakhstan
-
-
-
-
- Lao People's Democratic Republic
-
-
-
-
- Lebanon
-
-
-
-
- Saint Lucia
-
-
-
-
- Liechtenstein
-
-
-
-
- Sri Lanka
-
-
-
-
- Liberia
-
-
-
-
- Lesotho
-
-
-
-
- Lithuania
-
-
-
-
- Luxembourg
-
-
-
-
- Latvia
-
-
-
-
- Libyan Arab Jamahiriya
-
-
-
-
- Morocco
-
-
-
-
- Monaco
-
-
-
-
- Moldova, Republic of
-
-
-
-
- Montenegro
-
-
-
-
- Saint Martin
-
-
-
-
- Madagascar
-
-
-
-
- Marshall Islands
-
-
-
-
- Macedonia
-
-
-
-
- Mali
-
-
-
-
- Myanmar
-
-
-
-
- Mongolia
-
-
-
-
- Macao
-
-
-
-
- Northern Mariana Islands
-
-
-
-
- Martinique
-
-
-
-
- Mauritania
-
-
-
-
- Montserrat
-
-
-
-
- Malta
-
-
-
-
- Mauritius
-
-
-
-
- Maldives
-
-
-
-
- Malawi
-
-
-
-
- Mexico
-
-
-
-
- Malaysia
-
-
-
-
- Mozambique
-
-
-
-
- Namibia
-
-
-
-
- New Caledonia
-
-
-
-
- Niger
-
-
-
-
- Norfolk Island
-
-
-
-
- Nigeria
-
-
-
-
- Nicaragua
-
-
-
-
- Netherlands
-
-
-
-
- Norway
-
-
-
-
- Nepal
-
-
-
-
- Nauru
-
-
-
-
- Niue
-
-
-
-
- New Zealand
-
-
-
-
- Oman
-
-
-
-
- Panama
-
-
-
-
- Peru
-
-
-
-
- French Polynesia
-
-
-
-
- Papua New Guinea
-
-
-
-
- Philippines
-
-
-
-
- Pakistan
-
-
-
-
- Poland
-
-
-
-
- Saint Pierre and Miquelon
-
-
-
-
- Pitcairn
-
-
-
-
- Puerto Rico
-
-
-
-
- Palestinian Territory
-
-
-
-
- Portugal
-
-
-
-
- Palau
-
-
-
-
- Paraguay
-
-
-
-
- Qatar
-
-
-
-
- Reunion
-
-
-
-
- Romania
-
-
-
-
- Serbia
-
-
-
-
- Russian Federation
-
-
-
-
- Rwanda
-
-
-
-
- Saudi Arabia
-
-
-
-
- Solomon Islands
-
-
-
-
- Seychelles
-
-
-
-
- Sudan
-
-
-
-
- Sweden
-
-
-
-
- Singapore
-
-
-
-
- Saint Helena
-
-
-
-
- Slovenia
-
-
-
-
- Svalbard and Jan Mayen
-
-
-
-
- Slovakia
-
-
-
-
- Sierra Leone
-
-
-
-
- San Marino
-
-
-
-
- Senegal
-
-
-
-
- Somalia
-
-
-
-
- Suriname
-
-
-
-
- South Sudan
-
-
-
-
- Sao Tome and Principe
-
-
-
-
- El Salvador
-
-
-
-
- Sint Maarten (Dutch part)
-
-
-
-
- Syrian Arab Republic
-
-
-
-
- Swaziland
-
-
-
-
- Turks and Caicos Islands
-
-
-
-
- Chad
-
-
-
-
- French Southern Territories
-
-
-
-
- Togo
-
-
-
-
- Thailand
-
-
-
-
- Tajikistan
-
-
-
-
- Tokelau
-
-
-
-
- Timor-Leste
-
-
-
-
- Turkmenistan
-
-
-
-
- Tunisia
-
-
-
-
- Tonga
-
-
-
-
- Turkey
-
-
-
-
- Trinidad and Tobago
-
-
-
-
- Tuvalu
-
-
-
-
- Taiwan
-
-
-
-
- Tanzania, United Republic of
-
-
-
-
- Ukraine
-
-
-
-
- Uganda
-
-
-
-
- United States Minor Outlying Islands
-
-
-
-
- United States
-
-
-
-
- Uruguay
-
-
-
-
- Uzbekistan
-
-
-
-
- Holy See (Vatican City State)
-
-
-
-
- Saint Vincent and the Grenadines
-
-
-
-
- Venezuela
-
-
-
-
- Virgin Islands, British
-
-
-
-
- Virgin Islands, U.S.
-
-
-
-
- Vietnam
-
-
-
-
- Vanuatu
-
-
-
-
- Wallis and Futuna
-
-
-
-
- Samoa
-
-
-
-
- Kosovo
-
-
-
-
- Yemen
-
-
-
-
- Mayotte
-
-
-
-
- South Africa
-
-
-
-
- Zambia
-
-
-
-
- Zimbabwe
-
-
-
-
-
-
-
- This type supports the two digit codes of the US states and territories. They are enumerated in the XML schema.
-
-
-
-
-
- Alaska
-
-
-
-
- Alabama
-
-
-
-
- Arkansas
-
-
-
-
- Arizona
-
-
-
-
- California
-
-
-
-
- Colorado
-
-
-
-
- Connecticut
-
-
-
-
- District of Columbia
-
-
-
-
- Delaware
-
-
-
-
- Florida
-
-
-
-
- Georgia
-
-
-
-
- Guam
-
-
-
-
- Hawaii
-
-
-
-
- Iowa
-
-
-
-
- Idaho
-
-
-
-
- Illinois
-
-
-
-
- Indiana
-
-
-
-
- Kansas
-
-
-
-
- Kentucky
-
-
-
-
- Louisiana
-
-
-
-
- Massachusetts
-
-
-
-
- Maryland
-
-
-
-
- Maine
-
-
-
-
- Michigan
-
-
-
-
- Minnesota
-
-
-
-
- Missouri
-
-
-
-
- Mississippi
-
-
-
-
- Montana
-
-
-
-
- North Carolina
-
-
-
-
- North Dakota
-
-
-
-
- Nebraska
-
-
-
-
- New Hampshire
-
-
-
-
- New Jersey
-
-
-
-
- New Mexico
-
-
-
-
- Nevada
-
-
-
-
- New York
-
-
-
-
- Ohio
-
-
-
-
- Oklahoma
-
-
-
-
- Oregon
-
-
-
-
- Pennsylvania
-
-
-
-
- Puerto Rico
-
-
-
-
- Rhode Island
-
-
-
-
- South Carolina
-
-
-
-
- South Dakota
-
-
-
-
- Tennessee
-
-
-
-
- Texas
-
-
-
-
- Utah
-
-
-
-
- Virginia
-
-
-
-
- Virgin Islands
-
-
-
-
- Vermont
-
-
-
-
- Washington
-
-
-
-
- Wisconsin
-
-
-
-
- West Virginia
-
-
-
-
- Wyoming
-
-
-
-
-
-
-
- This element is the root element of the response to a report, file or file details submittal.
-
-
-
-
-
- The response code returned from the submittal. 0 is success, any non-zero number is an error code defined in the documentation.
-
-
-
-
- The description of the error code or empty if the response is success.
-
-
-
-
- The report ID about which this submittal is related.
-
-
-
-
- If a file was uploaded, the file ID of the file.
-
-
-
-
- If a file was uploaded, the md5 hash of the file.
-
-
-
-
-
-
-
-
- The final response from the server once the report has been submitted, all files and file details have been uploaded and the report done has been submitted.
-
-
-
-
-
- The response code returned from the submittal. 0 is success, any non-zero number is an error code defined in the documentation.
-
-
-
-
-
- The list of files successfully uploaded.
-
-
-
-
-
-
-
-
-
-
-
diff --git a/fluxer_api/src/api/test/fixtures/thisisfine.gif b/fluxer_api/src/api/test/fixtures/thisisfine.gif
deleted file mode 100644
index 6c8e2feb7..000000000
Binary files a/fluxer_api/src/api/test/fixtures/thisisfine.gif and /dev/null differ
diff --git a/fluxer_api/src/api/user/tests/FavoriteMemeLimits.test.ts b/fluxer_api/src/api/user/tests/FavoriteMemeLimits.test.ts
index 1bd4a7a5b..ac6bae003 100644
--- a/fluxer_api/src/api/user/tests/FavoriteMemeLimits.test.ts
+++ b/fluxer_api/src/api/user/tests/FavoriteMemeLimits.test.ts
@@ -113,13 +113,13 @@ describe('Favorite Meme Limits', () => {
attachment_id: message1.attachments[0].id,
name: 'PNG Meme',
});
- const message2 = await createMessageWithImageAttachment(harness, account.token, channel.id, 'thisisfine.gif');
+ const message2 = await createMessageWithImageAttachment(harness, account.token, channel.id, 'animated.gif');
const meme2 = await createFavoriteMemeFromMessage(harness, account.token, channel.id, message2.id, {
attachment_id: message2.attachments[0].id,
name: 'GIF Meme',
});
expect(meme2.id).toBeTruthy();
- expect(meme2.filename).toBe('thisisfine.gif');
+ expect(meme2.filename).toBe('animated.gif');
});
test('should return error for invalid attachment id', async () => {
const account = await createTestAccountForAttachmentTests(harness);
diff --git a/fluxer_api/src/api/user/tests/FavoriteMemeOperations.test.ts b/fluxer_api/src/api/user/tests/FavoriteMemeOperations.test.ts
index c40c6e516..3e65adf2c 100644
--- a/fluxer_api/src/api/user/tests/FavoriteMemeOperations.test.ts
+++ b/fluxer_api/src/api/user/tests/FavoriteMemeOperations.test.ts
@@ -244,7 +244,7 @@ describe('Favorite Meme Operations', () => {
attachment_id: message1.attachments[0].id,
name: 'First Meme',
});
- const message2 = await createMessageWithImageAttachment(harness, account.token, channel.id, 'thisisfine.gif');
+ const message2 = await createMessageWithImageAttachment(harness, account.token, channel.id, 'animated.gif');
await createFavoriteMemeFromMessage(harness, account.token, channel.id, message2.id, {
attachment_id: message2.attachments[0].id,
name: 'Second Meme',
diff --git a/fluxer_api/src/api/webhook/WebhookService.ts b/fluxer_api/src/api/webhook/WebhookService.ts
index 2df4f3a4b..579677f13 100644
--- a/fluxer_api/src/api/webhook/WebhookService.ts
+++ b/fluxer_api/src/api/webhook/WebhookService.ts
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
-import fs from 'node:fs/promises';
import {stripOwnAttachmentSignature} from '@app/api/attachment/AttachmentUrls';
import type {ChannelID, GuildID, MessageID, UserID, WebhookID, WebhookToken} from '@app/api/BrandedTypes';
import {createChannelID, createGuildID, createWebhookID, createWebhookToken} from '@app/api/BrandedTypes';
+import {Config} from '@app/api/Config';
import type {IChannelRepository} from '@app/api/channel/IChannelRepository';
import type {MessageRequest, MessageUpdateRequest} from '@app/api/channel/MessageTypes';
import {withChannelFollowLock} from '@app/api/channel/services/ChannelFollowers';
@@ -29,7 +29,6 @@ import type {RequestCache} from '@app/api/middleware/RequestCacheMiddleware';
import type {Channel} from '@app/api/models/Channel';
import type {Message} from '@app/api/models/Message';
import type {Webhook} from '@app/api/models/Webhook';
-import {resolveAssetPath} from '@app/api/utils/AssetPaths';
import * as RandomUtils from '@app/api/utils/RandomUtils';
import type {IWebhookRepository} from '@app/api/webhook/IWebhookRepository';
import {transform as GitHubTransform} from '@app/api/webhook/transformers/GitHubTransformer';
@@ -117,6 +116,10 @@ interface WebhookExecuteInstatusParams extends WebhookTokenParams {
}
const WEBHOOK_AVATAR_MISSING_CACHE_VALUE = '__fluxer_webhook_avatar_missing__';
+const HOSTED_WEBHOOK_AVATAR_URLS = {
+ github: 'https://fluxer.app/static/img/app-webhook-github.2d0319169a3aee33.webp',
+ instatus: 'https://fluxer.app/static/img/app-webhook-instatus.22ad5aee16da872c.webp',
+} as const;
export class WebhookService {
private static readonly NO_ALLOWED_MENTIONS: AllowedMentionsRequest = {parse: []};
@@ -744,32 +747,22 @@ export class WebhookService {
}
private async getGitHubWebhookAvatar(webhookId: WebhookID): Promise {
- return this.getStaticWebhookAvatar({webhookId, provider: 'github'});
+ return this.getHostedWebhookAvatar({webhookId, provider: 'github'});
}
private async getInstatusWebhookAvatar(webhookId: WebhookID): Promise {
- return this.getStaticWebhookAvatar({webhookId, provider: 'instatus'});
+ return this.getHostedWebhookAvatar({webhookId, provider: 'instatus'});
}
- private async getStaticWebhookAvatar({
+ private async getHostedWebhookAvatar({
webhookId,
provider,
}: {
webhookId: WebhookID;
- provider: 'github' | 'instatus';
+ provider: keyof typeof HOSTED_WEBHOOK_AVATAR_URLS;
}): Promise {
- const cacheKey = `webhook:${webhookId}:avatar:${provider}`;
- const avatarCache = await this.cacheService.get(cacheKey);
- if (avatarCache) return avatarCache;
- const avatarFile = await fs.readFile(resolveAssetPath('assets', `${provider}.webp`));
- const avatar = await this.avatarService.uploadAvatar({
- prefix: 'avatars',
- entityId: webhookId,
- errorPath: 'avatar',
- base64Image: avatarFile.toString('base64'),
- });
- await this.cacheService.set(cacheKey, avatar, seconds('1 day'));
- return avatar;
+ if (Config.instance.selfHosted) return null;
+ return this.getWebhookAvatar({webhookId, avatarUrl: HOSTED_WEBHOOK_AVATAR_URLS[provider]});
}
private getWebhookMetadata(webhook: Webhook): Record | undefined {
diff --git a/fluxer_api/src/api/webhook/tests/WebhookEmojiBypass.test.ts b/fluxer_api/src/api/webhook/tests/WebhookEmojiBypass.test.ts
index b0734e29d..48e1a5393 100644
--- a/fluxer_api/src/api/webhook/tests/WebhookEmojiBypass.test.ts
+++ b/fluxer_api/src/api/webhook/tests/WebhookEmojiBypass.test.ts
@@ -35,7 +35,7 @@ describe('Webhook emoji bypass', () => {
user.token,
guildId,
'animated',
- 'thisisfine.gif',
+ 'animated.gif',
'image/gif',
);
const webhook = await createWebhook(harness, channelId, user.token, 'Emoji Test Webhook');
diff --git a/fluxer_app/src/features/accessibility/components/NekoSprite.tsx b/fluxer_app/src/features/accessibility/components/NekoSprite.tsx
index 974e7a367..43ecf8716 100644
--- a/fluxer_app/src/features/accessibility/components/NekoSprite.tsx
+++ b/fluxer_app/src/features/accessibility/components/NekoSprite.tsx
@@ -1,4 +1,5 @@
-// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-License-Identifier: AGPL-3.0-or-later AND MIT
+// SPDX-FileCopyrightText: 2022 adryd
import styles from '@app/features/accessibility/components/NekoSprite.module.css';
import Accessibility from '@app/features/accessibility/state/Accessibility';
diff --git a/fluxer_app/src/features/app/config/HostedMedia.ts b/fluxer_app/src/features/app/config/HostedMedia.ts
new file mode 100644
index 000000000..e52a51011
--- /dev/null
+++ b/fluxer_app/src/features/app/config/HostedMedia.ts
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+export function hostedImageUrl(name: string): string {
+ return `https://fluxer.app/static/img/${name}`;
+}
diff --git a/fluxer_app/src/features/moderation/components/modals/BanMemberModal.module.css b/fluxer_app/src/features/moderation/components/modals/BanMemberModal.module.css
index 5ad088612..a05b49323 100644
--- a/fluxer_app/src/features/moderation/components/modals/BanMemberModal.module.css
+++ b/fluxer_app/src/features/moderation/components/modals/BanMemberModal.module.css
@@ -7,9 +7,13 @@
}
.video {
+ display: block;
width: 100%;
max-width: 25rem;
+ height: auto;
+ aspect-ratio: 2 / 1;
margin-bottom: 1rem;
+ object-fit: cover;
}
.sectionTitle {
diff --git a/fluxer_app/src/features/moderation/components/modals/BanMemberModal.tsx b/fluxer_app/src/features/moderation/components/modals/BanMemberModal.tsx
index 92f9aa59a..d64d0bba5 100644
--- a/fluxer_app/src/features/moderation/components/modals/BanMemberModal.tsx
+++ b/fluxer_app/src/features/moderation/components/modals/BanMemberModal.tsx
@@ -4,6 +4,7 @@ import Accessibility from '@app/features/accessibility/state/Accessibility';
import * as Modal from '@app/features/app/components/dialogs/Modal';
import {useAnimatedMediaVideoPlayback} from '@app/features/app/hooks/useAnimatedMediaPlayback';
import {useSaveData} from '@app/features/app/hooks/useSaveData';
+import RuntimeConfig from '@app/features/app/state/RuntimeConfig';
import * as GuildCommands from '@app/features/guild/commands/GuildCommands';
import {
CUSTOM_ELLIPSIS_DESCRIPTOR,
@@ -16,6 +17,7 @@ import {
import {showModerationErrorModal} from '@app/features/moderation/components/alerts/ModerationErrorModalUtils';
import styles from '@app/features/moderation/components/modals/BanMemberModal.module.css';
import {BAN_DELETE_MESSAGE_OPTIONS} from '@app/features/moderation/constants/BanDeleteMessageOptions';
+import {BANNED_MP4, BANNED_POSTER, BANNED_WEBM} from '@app/features/moderation/constants/BanModalMedia';
import {Logger} from '@app/features/platform/utils/AppLogger';
import {Button} from '@app/features/ui/button/Button';
import * as ModalCommands from '@app/features/ui/commands/ModalCommands';
@@ -25,9 +27,6 @@ import {Input} from '@app/features/ui/components/form/FormInput';
import {RadioGroup} from '@app/features/ui/radio_group/RadioGroup';
import type {User} from '@app/features/user/models/User';
import * as DisplayNameUtils from '@app/features/user/utils/DisplayNameUtils';
-import bannedMp4 from '@app/media/videos/banned.mp4';
-import bannedWebm from '@app/media/videos/banned.webm';
-import bannedPoster from '@app/media/videos/banned.webp';
import {MAX_TEMP_BAN_DURATION_SECONDS, MIN_TEMP_BAN_DURATION_SECONDS} from '@fluxer/constants/src/LimitConstants';
import {msg} from '@lingui/core/macro';
import {Trans, useLingui} from '@lingui/react/macro';
@@ -101,7 +100,8 @@ export const BanMemberModal: React.FC<{guildId: string; targetUser: User}> = obs
const {i18n} = useLingui();
const videoRef = useRef(null);
const dataSaverOn = useSaveData();
- const motionArtworkAllowed = !Accessibility.useReducedMotion && !dataSaverOn;
+ const showArtwork = !RuntimeConfig.isSelfHosted();
+ const motionArtworkAllowed = showArtwork && !Accessibility.useReducedMotion && !dataSaverOn;
const videoPlaybackAllowed = useAnimatedMediaVideoPlayback(videoRef, {enabled: motionArtworkAllowed});
const [reason, setReason] = useState('');
const [deleteMessageSeconds, setDeleteMessageSeconds] = useState(60 * 60 * 24);
@@ -160,22 +160,26 @@ export const BanMemberModal: React.FC<{guildId: string; targetUser: User}> = obs
loop
muted
playsInline
- poster={bannedPoster}
+ poster={BANNED_POSTER}
+ width={480}
+ height={240}
className={styles.video}
data-flx="moderation.ban-member-modal.video"
>
-
-
+
+
- ) : (
+ ) : showArtwork ? (
- )}
+ ) : null}
label={i18n._(BAN_DURATION_DESCRIPTOR)}
diff --git a/fluxer_app/src/features/moderation/constants/BanModalMedia.ts b/fluxer_app/src/features/moderation/constants/BanModalMedia.ts
new file mode 100644
index 000000000..ca4dc28d8
--- /dev/null
+++ b/fluxer_app/src/features/moderation/constants/BanModalMedia.ts
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+import {hostedImageUrl} from '@app/features/app/config/HostedMedia';
+
+export const BANNED_MP4 = hostedImageUrl('app-banned.57f4a7f02835e746.mp4');
+export const BANNED_WEBM = hostedImageUrl('app-banned.7b2a4c17603fbe1c.webm');
+export const BANNED_POSTER = hostedImageUrl('app-banned.da3d66d03a7c4962.webp');
diff --git a/fluxer_app/src/features/premium/components/plutonium_page/PlutoniumPage.module.css b/fluxer_app/src/features/premium/components/plutonium_page/PlutoniumPage.module.css
index 290f66150..f69b8a114 100644
--- a/fluxer_app/src/features/premium/components/plutonium_page/PlutoniumPage.module.css
+++ b/fluxer_app/src/features/premium/components/plutonium_page/PlutoniumPage.module.css
@@ -778,6 +778,11 @@
.perkRowFlipped {
grid-template-columns: 5fr 7fr;
}
+
+ .perkRowTextOnly {
+ grid-template-columns: minmax(0, 1fr);
+ padding-block: 2.5rem;
+ }
}
.perkArtColumn {
diff --git a/fluxer_app/src/features/premium/components/plutonium_page/PlutoniumPage.tsx b/fluxer_app/src/features/premium/components/plutonium_page/PlutoniumPage.tsx
index 26b7a3037..a81b5f18d 100644
--- a/fluxer_app/src/features/premium/components/plutonium_page/PlutoniumPage.tsx
+++ b/fluxer_app/src/features/premium/components/plutonium_page/PlutoniumPage.tsx
@@ -30,6 +30,7 @@ import {
resolveUploadSizes,
} from '@app/features/premium/components/plutonium_page/PlutoniumPageComparison';
import {PlutoniumPageIcon} from '@app/features/premium/components/plutonium_page/PlutoniumPageIcons';
+import {CROWN_AVIF, CROWN_WEBP} from '@app/features/premium/components/plutonium_page/PlutoniumPageMedia';
import {
BACK_DESCRIPTOR,
CLOSING_BODY_DESCRIPTOR,
@@ -74,8 +75,6 @@ import FocusRing from '@app/features/ui/focus_ring/FocusRing';
import MobileLayout from '@app/features/ui/state/MobileLayout';
import Users from '@app/features/user/state/Users';
import * as LocaleUtils from '@app/features/user/utils/LocaleUtils';
-import crownAvif from '@app/media/images/plutonium/plutonium-crown.avif';
-import crownWebp from '@app/media/images/plutonium/plutonium-crown.webp';
import {GuildFeatures} from '@fluxer/constants/src/GuildConstants';
import {getFormattedLongDate} from '@fluxer/date_utils/src/DateFormatting';
import {Trans, useLingui} from '@lingui/react/macro';
@@ -451,22 +450,24 @@ export const PlutoniumPage = observer(function PlutoniumPage() {
)}