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();
|
||||
}
|
||||
}
|
||||
|
||||
async hasSeenHoundActivity(challengeId: string, ...activityIds: string[]): Promise<string[]> {
|
||||
const existing = this.houndActivityIds.get(challengeId);
|
||||
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> {
|
||||
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 ISSUES_EXPIRE_AFTER = 7 * 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.";
|
||||
@ -245,11 +245,19 @@ export class RedisStorageProvider extends RedisStorageContextualProvider impleme
|
||||
}
|
||||
|
||||
public async storeHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<void> {
|
||||
if (activityHashes.length === 0) {
|
||||
return;
|
||||
}
|
||||
const key = `${HOUND_GUIDS}${challengeId}`;
|
||||
await this.redis.lpush(key, ...activityHashes);
|
||||
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[]> {
|
||||
let multi = this.redis.multi();
|
||||
const key = `${HOUND_GUIDS}${challengeId}`;
|
||||
|
@ -32,6 +32,7 @@ export interface IBridgeStorageProvider extends IAppserviceStorageProvider, ISto
|
||||
hasSeenFeedGuids(url: string, ...guids: string[]): Promise<string[]>;
|
||||
|
||||
storeHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<void>;
|
||||
hasSeenHoundChallenge(challengeId: string): Promise<boolean>;
|
||||
hasSeenHoundActivity(challengeId: string, ...activityHashes: string[]): Promise<string[]>;
|
||||
storeHoundActivityEvent(challengeId: string, activityId: string, eventId: string): Promise<void>;
|
||||
getHoundActivity(challengeId: string, activityId: string): Promise<string|null>;
|
||||
|
@ -113,13 +113,10 @@ impl BridgePermissions {
|
||||
continue;
|
||||
}
|
||||
for actor_service in actor_permission.services.iter() {
|
||||
match &actor_service.service {
|
||||
Some(actor_service_service) => {
|
||||
if actor_service_service != &service && actor_service_service != "*" {
|
||||
continue;
|
||||
}
|
||||
if let Some(actor_service_service) = &actor_service.service {
|
||||
if actor_service_service != &service && actor_service_service != "*" {
|
||||
continue;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if permission_level_to_int(actor_service.level.clone())? >= permission_int {
|
||||
return Ok(true);
|
||||
|
@ -174,9 +174,9 @@ pub fn hash_id(id: String) -> Result<String> {
|
||||
|
||||
#[napi(js_name = "sanitizeHtml")]
|
||||
pub fn hookshot_sanitize_html(html: String) -> String {
|
||||
return sanitize_html(
|
||||
sanitize_html(
|
||||
html.as_str(),
|
||||
HtmlSanitizerMode::Compat,
|
||||
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 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));
|
||||
for (const activity of activites) {
|
||||
if (seen.includes(activity.hash)) {
|
||||
continue;
|
||||
}
|
||||
this.queue.push<HoundPayload>({
|
||||
eventName: "hound.activity",
|
||||
sender: "HoundReader",
|
||||
data: {
|
||||
challengeId,
|
||||
activity: activity,
|
||||
|
||||
// Don't emit anything if our cache is empty, as we'll probably create duplicates.
|
||||
const hasSeenChallenge = await this.storage.hasSeenHoundChallenge(challengeId);
|
||||
if (hasSeenChallenge) {
|
||||
for (const activity of activites) {
|
||||
if (seen.includes(activity.hash)) {
|
||||
continue;
|
||||
}
|
||||
});
|
||||
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> {
|
||||
|
Loading…
x
Reference in New Issue
Block a user