mirror of
https://github.com/matrix-org/matrix-hookshot.git
synced 2025-03-10 13:17:08 +00:00
Fix challengehound duplication bug. (#982)
* Fix challengehound duplication bug. * Missed a bit * No-op if no hashes need to be pushed. * changelog * lint
This commit is contained in:
parent
a9d8044633
commit
6571b9f710
1
changelog.d/982.bugfix
Normal file
1
changelog.d/982.bugfix
Normal file
@ -0,0 +1 @@
|
|||||||
|
Fix Challenge Hound activities being duplicated if the cache layer (e.g Redis) goes away.
|
@ -122,11 +122,16 @@ export class MemoryStorageProvider extends MSP implements IBridgeStorageProvider
|
|||||||
set.pop();
|
set.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasSeenHoundActivity(challengeId: string, ...activityIds: string[]): Promise<string[]> {
|
async hasSeenHoundActivity(challengeId: string, ...activityIds: string[]): Promise<string[]> {
|
||||||
const existing = this.houndActivityIds.get(challengeId);
|
const existing = this.houndActivityIds.get(challengeId);
|
||||||
return existing ? activityIds.filter((existingGuid) => existing.includes(existingGuid)) : [];
|
return existing ? activityIds.filter((existingGuid) => existing.includes(existingGuid)) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async hasSeenHoundChallenge(challengeId: string): Promise<boolean> {
|
||||||
|
return this.houndActivityIds.has(challengeId);
|
||||||
|
}
|
||||||
|
|
||||||
public async storeHoundActivityEvent(challengeId: string, activityId: string, eventId: string): Promise<void> {
|
public async storeHoundActivityEvent(challengeId: string, activityId: string, eventId: string): Promise<void> {
|
||||||
this.houndActivityIdToEvent.set(`${challengeId}.${activityId}`, eventId);
|
this.houndActivityIdToEvent.set(`${challengeId}.${activityId}`, eventId);
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ const STORED_FILES_EXPIRE_AFTER = 24 * 60 * 60; // 24 hours
|
|||||||
const COMPLETED_TRANSACTIONS_EXPIRE_AFTER = 24 * 60 * 60; // 24 hours
|
const COMPLETED_TRANSACTIONS_EXPIRE_AFTER = 24 * 60 * 60; // 24 hours
|
||||||
const ISSUES_EXPIRE_AFTER = 7 * 24 * 60 * 60; // 7 days
|
const ISSUES_EXPIRE_AFTER = 7 * 24 * 60 * 60; // 7 days
|
||||||
const ISSUES_LAST_COMMENT_EXPIRE_AFTER = 14 * 24 * 60 * 60; // 7 days
|
const ISSUES_LAST_COMMENT_EXPIRE_AFTER = 14 * 24 * 60 * 60; // 7 days
|
||||||
const HOUND_EVENT_CACHE = 90 * 24 * 60 * 60; // 30 days
|
const HOUND_EVENT_CACHE = 90 * 24 * 60 * 60; // 90 days
|
||||||
|
|
||||||
|
|
||||||
const WIDGET_TOKENS = "widgets.tokens.";
|
const WIDGET_TOKENS = "widgets.tokens.";
|
||||||
@ -245,11 +245,19 @@ export class RedisStorageProvider extends RedisStorageContextualProvider impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async storeHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<void> {
|
public async storeHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<void> {
|
||||||
|
if (activityHashes.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const key = `${HOUND_GUIDS}${challengeId}`;
|
const key = `${HOUND_GUIDS}${challengeId}`;
|
||||||
await this.redis.lpush(key, ...activityHashes);
|
await this.redis.lpush(key, ...activityHashes);
|
||||||
await this.redis.ltrim(key, 0, MAX_FEED_ITEMS);
|
await this.redis.ltrim(key, 0, MAX_FEED_ITEMS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async hasSeenHoundChallenge(challengeId: string): Promise<boolean> {
|
||||||
|
const key = `${HOUND_GUIDS}${challengeId}`;
|
||||||
|
return (await this.redis.exists(key)) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
public async hasSeenHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<string[]> {
|
public async hasSeenHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<string[]> {
|
||||||
let multi = this.redis.multi();
|
let multi = this.redis.multi();
|
||||||
const key = `${HOUND_GUIDS}${challengeId}`;
|
const key = `${HOUND_GUIDS}${challengeId}`;
|
||||||
|
@ -32,6 +32,7 @@ export interface IBridgeStorageProvider extends IAppserviceStorageProvider, ISto
|
|||||||
hasSeenFeedGuids(url: string, ...guids: string[]): Promise<string[]>;
|
hasSeenFeedGuids(url: string, ...guids: string[]): Promise<string[]>;
|
||||||
|
|
||||||
storeHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<void>;
|
storeHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<void>;
|
||||||
|
hasSeenHoundChallenge(challengeId: string): Promise<boolean>;
|
||||||
hasSeenHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<string[]>;
|
hasSeenHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<string[]>;
|
||||||
storeHoundActivityEvent(challengeId: string, activityId: string, eventId: string): Promise<void>;
|
storeHoundActivityEvent(challengeId: string, activityId: string, eventId: string): Promise<void>;
|
||||||
getHoundActivity(challengeId: string, activityId: string): Promise<string|null>;
|
getHoundActivity(challengeId: string, activityId: string): Promise<string|null>;
|
||||||
|
@ -113,13 +113,10 @@ impl BridgePermissions {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for actor_service in actor_permission.services.iter() {
|
for actor_service in actor_permission.services.iter() {
|
||||||
match &actor_service.service {
|
if let Some(actor_service_service) = &actor_service.service {
|
||||||
Some(actor_service_service) => {
|
if actor_service_service != &service && actor_service_service != "*" {
|
||||||
if actor_service_service != &service && actor_service_service != "*" {
|
continue;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
if permission_level_to_int(actor_service.level.clone())? >= permission_int {
|
if permission_level_to_int(actor_service.level.clone())? >= permission_int {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
|
@ -174,9 +174,9 @@ pub fn hash_id(id: String) -> Result<String> {
|
|||||||
|
|
||||||
#[napi(js_name = "sanitizeHtml")]
|
#[napi(js_name = "sanitizeHtml")]
|
||||||
pub fn hookshot_sanitize_html(html: String) -> String {
|
pub fn hookshot_sanitize_html(html: String) -> String {
|
||||||
return sanitize_html(
|
sanitize_html(
|
||||||
html.as_str(),
|
html.as_str(),
|
||||||
HtmlSanitizerMode::Compat,
|
HtmlSanitizerMode::Compat,
|
||||||
RemoveReplyFallback::No,
|
RemoveReplyFallback::No,
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -83,20 +83,26 @@ export class HoundReader {
|
|||||||
const resAct = await this.houndClient.get(`https://api.challengehound.com/v1/activities?challengeId=${challengeId}&size=10`);
|
const resAct = await this.houndClient.get(`https://api.challengehound.com/v1/activities?challengeId=${challengeId}&size=10`);
|
||||||
const activites = (resAct.data["results"] as HoundActivity[]).map(a => ({...a, hash: HoundReader.hashActivity(a)}));
|
const activites = (resAct.data["results"] as HoundActivity[]).map(a => ({...a, hash: HoundReader.hashActivity(a)}));
|
||||||
const seen = await this.storage.hasSeenHoundActivity(challengeId, ...activites.map(a => a.hash));
|
const seen = await this.storage.hasSeenHoundActivity(challengeId, ...activites.map(a => a.hash));
|
||||||
for (const activity of activites) {
|
|
||||||
if (seen.includes(activity.hash)) {
|
// Don't emit anything if our cache is empty, as we'll probably create duplicates.
|
||||||
continue;
|
const hasSeenChallenge = await this.storage.hasSeenHoundChallenge(challengeId);
|
||||||
}
|
if (hasSeenChallenge) {
|
||||||
this.queue.push<HoundPayload>({
|
for (const activity of activites) {
|
||||||
eventName: "hound.activity",
|
if (seen.includes(activity.hash)) {
|
||||||
sender: "HoundReader",
|
continue;
|
||||||
data: {
|
|
||||||
challengeId,
|
|
||||||
activity: activity,
|
|
||||||
}
|
}
|
||||||
});
|
this.queue.push<HoundPayload>({
|
||||||
|
eventName: "hound.activity",
|
||||||
|
sender: "HoundReader",
|
||||||
|
data: {
|
||||||
|
challengeId,
|
||||||
|
activity: activity,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await this.storage.storeHoundActivity(challengeId, ...activites.map(a => a.hash))
|
// Ensure we don't add duplicates to the storage.
|
||||||
|
await this.storage.storeHoundActivity(challengeId, ...activites.filter(s => !seen.includes(s.hash)).map(a => a.hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
public async pollChallenges(): Promise<void> {
|
public async pollChallenges(): Promise<void> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user