From c135d1602441c4f2db689a479a19869aec76e616 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sat, 11 Feb 2023 22:50:15 +0100 Subject: [PATCH 01/11] meow Co-authored-by: cutls --- packages/backend/package.json | 2 +- .../server/api/mastodon/endpoints/account.ts | 19 +++++++++++++++++++ pnpm-lock.yaml | 8 ++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index c1af9b1730..7a25ee76b8 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -79,7 +79,7 @@ "koa-send": "5.0.1", "koa-slow": "2.1.0", "koa-views": "7.0.2", - "@cutls/megalodon": "5.1.15", + "@cutls/megalodon": "5.1.16", "mfm-js": "0.23.2", "mime-types": "2.1.35", "multer": "1.4.4-lts.1", diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 0162951d63..2766dbdcee 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -4,6 +4,23 @@ import { koaBody } from "koa-body"; import { getClient } from "../ApiMastodonCompatibleService.js"; import { toLimitToInt } from "./timeline.js"; +const relationshopModel = { + id: '', + following: false, + followed_by: false, + delivery_following: false, + blocking: false, + blocked_by: false, + muting: false, + muting_notifications: false, + requested: false, + domain_blocking: false, + showing_reblogs: false, + endorsed: false, + notifying: false, + note: '' +} + export function apiAccountMastodon(router: Router): void { router.get("/v1/accounts/verify_credentials", async (ctx, next) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; @@ -258,6 +275,8 @@ export function apiAccountMastodon(router: Router): void { try { const idsRaw = (ctx.query as any)["id[]"]; const ids = typeof idsRaw === "string" ? [idsRaw] : idsRaw; + relationshopModel.id = idsRaw || '1' + if (!idsRaw) return [relationshopModel] const data = (await client.getRelationships(ids)) as any; ctx.body = data.data; } catch (e: any) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 056b02cbfe..a4a61060ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,7 +57,7 @@ importers: '@bull-board/api': ^4.6.4 '@bull-board/koa': ^4.6.4 '@bull-board/ui': ^4.6.4 - '@cutls/megalodon': 5.1.15 + '@cutls/megalodon': 5.1.16 '@discordapp/twemoji': 14.0.2 '@elastic/elasticsearch': 7.17.0 '@koa/cors': 3.4.3 @@ -223,7 +223,7 @@ importers: '@bull-board/api': 4.10.2 '@bull-board/koa': 4.10.2_6tybghmia4wsnt33xeid7y4rby '@bull-board/ui': 4.10.2 - '@cutls/megalodon': 5.1.15 + '@cutls/megalodon': 5.1.16 '@discordapp/twemoji': 14.0.2 '@elastic/elasticsearch': 7.17.0 '@koa/cors': 3.4.3 @@ -847,8 +847,8 @@ packages: dependencies: '@jridgewell/trace-mapping': 0.3.9 - /@cutls/megalodon/5.1.15: - resolution: {integrity: sha512-4+mIKUYYr2CLY3idSxXk56WSTG9ww3opeenmsPRxftTwcjQTYxGntNkWmJWEbzeJ4rPslnvpwD7cFR62bPf41g==} + /@cutls/megalodon/5.1.16: + resolution: {integrity: sha512-s2U3qjcs86v0/p0EPwlQqSMh4UhAqTNxu8xUVsPvzrwXr5TrrJ5AVKunBm5582AEkx47nZkqm/98ZF2iHb0GtQ==} engines: {node: '>=15.0.0'} dependencies: '@types/oauth': 0.9.1 From 43c985d78c0abf71bf3aaf23d4bb5da3efb518a8 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sat, 11 Feb 2023 23:12:14 +0100 Subject: [PATCH 02/11] fix timelines --- packages/backend/src/server/api/mastodon/endpoints/timeline.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts index 9caf431143..a2857e32ca 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts @@ -6,8 +6,9 @@ import Autolinker from "autolinker"; import { ParsedUrlQuery } from "querystring"; export function toLimitToInt(q: ParsedUrlQuery) { + let object: any = q; if (q.limit) - if (typeof q.limit === "string") q.limit = parseInt(q.limit, 10).toString(); + if (typeof q.limit === "string") object.limit = parseInt(q.limit, 10); return q; } From f61b157a70b0a2ec68873b65e7ad445bca1f5d26 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sat, 11 Feb 2023 23:46:35 +0100 Subject: [PATCH 03/11] change name of Reactions bot --- packages/backend/src/server/api/mastodon/endpoints/status.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index 3afd7e5769..cce2e8e1da 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -435,7 +435,7 @@ export function statusModel( id: "9arzuvv0sw", username: "ReactionBot", acct: "ReactionBot", - display_name: "ReactionOfThisPost", + display_name: "ReactionsToThisPost", locked: false, created_at: now, followers_count: 0, From 54de8b92d81f5a4b8a718454d83ff78c33949d61 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sat, 11 Feb 2023 23:50:58 +0100 Subject: [PATCH 04/11] remove unneeded next middleware --- .../server/api/mastodon/endpoints/account.ts | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 2766dbdcee..58de9ad178 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -1,6 +1,4 @@ -import megalodon, { MegalodonInterface } from "@cutls/megalodon"; import Router from "@koa/router"; -import { koaBody } from "koa-body"; import { getClient } from "../ApiMastodonCompatibleService.js"; import { toLimitToInt } from "./timeline.js"; @@ -22,7 +20,7 @@ const relationshopModel = { } export function apiAccountMastodon(router: Router): void { - router.get("/v1/accounts/verify_credentials", async (ctx, next) => { + router.get("/v1/accounts/verify_credentials", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -67,7 +65,7 @@ export function apiAccountMastodon(router: Router): void { }); router.get<{ Params: { id: string } }>( "/v1/accounts/:id", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -84,7 +82,7 @@ export function apiAccountMastodon(router: Router): void { ); router.get<{ Params: { id: string } }>( "/v1/accounts/:id/statuses", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -104,7 +102,7 @@ export function apiAccountMastodon(router: Router): void { ); router.get<{ Params: { id: string } }>( "/v1/accounts/:id/followers", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -124,7 +122,7 @@ export function apiAccountMastodon(router: Router): void { ); router.get<{ Params: { id: string } }>( "/v1/accounts/:id/following", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -144,7 +142,7 @@ export function apiAccountMastodon(router: Router): void { ); router.get<{ Params: { id: string } }>( "/v1/accounts/:id/lists", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -161,7 +159,7 @@ export function apiAccountMastodon(router: Router): void { ); router.post<{ Params: { id: string } }>( "/v1/accounts/:id/follow", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -180,7 +178,7 @@ export function apiAccountMastodon(router: Router): void { ); router.post<{ Params: { id: string } }>( "/v1/accounts/:id/unfollow", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -199,7 +197,7 @@ export function apiAccountMastodon(router: Router): void { ); router.post<{ Params: { id: string } }>( "/v1/accounts/:id/block", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -216,7 +214,7 @@ export function apiAccountMastodon(router: Router): void { ); router.post<{ Params: { id: string } }>( "/v1/accounts/:id/unblock", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -253,7 +251,7 @@ export function apiAccountMastodon(router: Router): void { ); router.post<{ Params: { id: string } }>( "/v1/accounts/:id/unmute", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -268,7 +266,7 @@ export function apiAccountMastodon(router: Router): void { } }, ); - router.get("/v1/accounts/relationships", async (ctx, next) => { + router.get("/v1/accounts/relationships", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -286,7 +284,7 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); - router.get("/v1/bookmarks", async (ctx, next) => { + router.get("/v1/bookmarks", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -300,7 +298,7 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); - router.get("/v1/favourites", async (ctx, next) => { + router.get("/v1/favourites", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -314,7 +312,7 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); - router.get("/v1/mutes", async (ctx, next) => { + router.get("/v1/mutes", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -328,7 +326,7 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); - router.get("/v1/blocks", async (ctx, next) => { + router.get("/v1/blocks", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -342,7 +340,7 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); - router.get("/v1/follow_ctxs", async (ctx, next) => { + router.get("/v1/follow_ctxs", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -360,7 +358,7 @@ export function apiAccountMastodon(router: Router): void { }); router.post<{ Params: { id: string } }>( "/v1/follow_ctxs/:id/authorize", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -377,7 +375,7 @@ export function apiAccountMastodon(router: Router): void { ); router.post<{ Params: { id: string } }>( "/v1/follow_ctxs/:id/reject", - async (ctx, next) => { + async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); From 631355c2c8dc4c0ad4188b8e1fc175f20c65661e Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sun, 12 Feb 2023 00:10:10 +0100 Subject: [PATCH 05/11] add account lookup --- .../server/api/mastodon/endpoints/account.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 58de9ad178..82c721ef47 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -63,6 +63,31 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); + router.get( + "/v1/accounts/lookup", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.searchAccount((ctx.query.acct || '').toString(), { + resolve: true + }); + ctx.body = data.data[0]; + if (data.data.length === 0) { + ctx.status = 404; + ctx.body = { + error: "Record not found" + } + } + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); router.get<{ Params: { id: string } }>( "/v1/accounts/:id", async (ctx) => { From 209cd8a7c8892e0a066a34af0aa3b6298b06d4b3 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sun, 12 Feb 2023 00:14:03 +0100 Subject: [PATCH 06/11] refine error msg --- packages/backend/src/server/api/mastodon/endpoints/account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 82c721ef47..cb9f6c4efe 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -77,7 +77,7 @@ export function apiAccountMastodon(router: Router): void { if (data.data.length === 0) { ctx.status = 404; ctx.body = { - error: "Record not found" + error: `Record (${ctx.query.acct}) not found` } } } catch (e: any) { From 7ea17df2ba04e5da9b876f0638d67af5f6a2b532 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sun, 12 Feb 2023 00:19:35 +0100 Subject: [PATCH 07/11] try this instead --- .../backend/src/server/api/mastodon/endpoints/account.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index cb9f6c4efe..fe48e4a116 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -70,11 +70,11 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.searchAccount((ctx.query.acct || '').toString(), { + const data = await client.search((ctx.query.acct || '').toString(), "accounts", { resolve: true }); - ctx.body = data.data[0]; - if (data.data.length === 0) { + ctx.body = data.data.accounts[0]; + if (data.data.accounts.length === 0) { ctx.status = 404; ctx.body = { error: `Record (${ctx.query.acct}) not found` From 63cd831cf62674476f1b292680b1f966f67369c9 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sun, 12 Feb 2023 00:25:51 +0100 Subject: [PATCH 08/11] simplify code --- .../server/api/mastodon/endpoints/account.ts | 33 ++++--------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index fe48e4a116..4e2c93334e 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -63,39 +63,20 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); - router.get( - "/v1/accounts/lookup", - async (ctx) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.search((ctx.query.acct || '').toString(), "accounts", { - resolve: true - }); - ctx.body = data.data.accounts[0]; - if (data.data.accounts.length === 0) { - ctx.status = 404; - ctx.body = { - error: `Record (${ctx.query.acct}) not found` - } - } - } catch (e: any) { - console.error(e); - console.error(e.response.data); - ctx.status = 401; - ctx.body = e.response.data; - } - }, - ); router.get<{ Params: { id: string } }>( "/v1/accounts/:id", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); + let req_user = ctx.query.acct; + if (!req_user) { + req_user = ctx.params.id; + } else { + req_user = req_user.toString(); + } try { - const data = await client.getAccount(ctx.params.id); + const data = await client.getAccount(req_user); ctx.body = data.data; } catch (e: any) { console.error(e); From b862402ce924ba34169104fbe19cb3496b055357 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sun, 12 Feb 2023 00:29:52 +0100 Subject: [PATCH 09/11] eow --- .../server/api/mastodon/endpoints/account.ts | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 4e2c93334e..2836c7fda3 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -63,20 +63,31 @@ export function apiAccountMastodon(router: Router): void { ctx.body = e.response.data; } }); + router.get( + "/v1/accounts/lookup", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getAccount(( || '').toString()); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); router.get<{ Params: { id: string } }>( "/v1/accounts/:id", async (ctx) => { const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); - let req_user = ctx.query.acct; - if (!req_user) { - req_user = ctx.params.id; - } else { - req_user = req_user.toString(); - } try { - const data = await client.getAccount(req_user); + const data = await client.getAccount(ctx.params.id); ctx.body = data.data; } catch (e: any) { console.error(e); From 69efbf8fdafa03b7e289ca16e579a9a10bfc09e9 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sun, 12 Feb 2023 00:33:52 +0100 Subject: [PATCH 10/11] oof --- packages/backend/src/server/api/mastodon/endpoints/account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 2836c7fda3..ef9ac4f433 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -70,7 +70,7 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getAccount(( || '').toString()); + const data = await client.getAccount((ctx.query.acct || '').toString()); ctx.body = data.data; } catch (e: any) { console.error(e); From 6eee77cbc5394448966cc879823a347942e05522 Mon Sep 17 00:00:00 2001 From: cutestnekoaqua Date: Sun, 12 Feb 2023 00:35:39 +0100 Subject: [PATCH 11/11] is this too spec? --- packages/backend/src/server/api/mastodon/endpoints/account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index ef9ac4f433..1127fa5fbd 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -70,7 +70,7 @@ export function apiAccountMastodon(router: Router): void { const accessTokens = ctx.headers.authorization; const client = getClient(BASE_URL, accessTokens); try { - const data = await client.getAccount((ctx.query.acct || '').toString()); + const data = await client.getAccount(`@${(ctx.query.acct || '').toString()}`); ctx.body = data.data; } catch (e: any) { console.error(e);