|
@@ -254,28 +254,28 @@ export default class {
|
|
|
|
|
|
const workNow = (config: {
|
|
const workNow = (config: {
|
|
rawUserName: string,
|
|
rawUserName: string,
|
|
- action: (userName: string) => void,
|
|
|
|
|
|
+ action: (userId: number | string) => void,
|
|
retryAction: () => void,
|
|
retryAction: () => void,
|
|
reply: (msg: string) => void
|
|
reply: (msg: string) => void
|
|
}) => {
|
|
}) => {
|
|
const {action, retryAction, reply, rawUserName} = config;
|
|
const {action, retryAction, reply, rawUserName} = config;
|
|
return this.queryUser(rawUserName)
|
|
return this.queryUser(rawUserName)
|
|
.then(userNameId => {
|
|
.then(userNameId => {
|
|
- const [userName, userId] = userNameId.split(':');
|
|
|
|
- if (Date.now() - this.cache[userName]?.updated?.getTime() > this.workInterval * 1000 &&
|
|
|
|
- Object.keys(this.cache[userName].stories).length > 0) {
|
|
|
|
- return userName;
|
|
|
|
|
|
+ const userId = userNameId.split(':')[1];
|
|
|
|
+ if (Date.now() - this.cache[userId]?.updated?.getTime() > this.workInterval * 1000 &&
|
|
|
|
+ Object.keys(this.cache[userId].stories).length > 0) {
|
|
|
|
+ return userId;
|
|
}
|
|
}
|
|
return this.client.feed.reelsMedia({userIds: [userId]}).items()
|
|
return this.client.feed.reelsMedia({userIds: [userId]}).items()
|
|
.then(storyItems => Promise.all(storyItems
|
|
.then(storyItems => Promise.all(storyItems
|
|
- .filter(item => !(item.pk in this.cache[userName].stories))
|
|
|
|
|
|
+ .filter(item => !(item.pk in this.cache[userId].stories))
|
|
.map(item => this.webshot(
|
|
.map(item => this.webshot(
|
|
- [{...item, user: this.cache[userName].user}],
|
|
|
|
|
|
+ [{...item, user: this.cache[userId].user}],
|
|
(msgs: string, text: string, author: string) =>
|
|
(msgs: string, text: string, author: string) =>
|
|
- this.cache[userName].stories[item.pk] = {pk: item.pk, msgs, text, author, original: item},
|
|
|
|
|
|
+ this.cache[userId].stories[item.pk] = {pk: item.pk, msgs, text, author, original: item},
|
|
this.webshotDelay
|
|
this.webshotDelay
|
|
))
|
|
))
|
|
- ).then(() => userName).finally(() => this.cache[userName].updated = new Date()));
|
|
|
|
|
|
+ ).then(() => userId).finally(() => this.cache[userId].updated = new Date()));
|
|
})
|
|
})
|
|
.then(action)
|
|
.then(action)
|
|
.catch((error: IgClientError & Partial<RequestError>) => {
|
|
.catch((error: IgClientError & Partial<RequestError>) => {
|
|
@@ -305,8 +305,9 @@ export default class {
|
|
const reply = msg => this.bot.sendTo(receiver, msg);
|
|
const reply = msg => this.bot.sendTo(receiver, msg);
|
|
workNow({
|
|
workNow({
|
|
rawUserName,
|
|
rawUserName,
|
|
- action: userName => {
|
|
|
|
- const storyItems = Object.values(this.cache[userName].stories)
|
|
|
|
|
|
+ action: userId => {
|
|
|
|
+ const userName = this.cache[userId].user.username;
|
|
|
|
+ const storyItems = Object.values(this.cache[userId].stories)
|
|
.sort((i1, i2) => -BigNumOps.compare(i2.pk, i1.pk)); // ascending!
|
|
.sort((i1, i2) => -BigNumOps.compare(i2.pk, i1.pk)); // ascending!
|
|
if (storyItems.length === 0) return reply(`当前用户 (@${userName}) 没有可用的 Instagram 限时动态。`);
|
|
if (storyItems.length === 0) return reply(`当前用户 (@${userName}) 没有可用的 Instagram 限时动态。`);
|
|
return reply('#. 编号:发送时间\n' + storyItems.map(({original}, index) =>
|
|
return reply('#. 编号:发送时间\n' + storyItems.map(({original}, index) =>
|
|
@@ -324,9 +325,9 @@ export default class {
|
|
const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
|
|
const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
|
|
workNow({
|
|
workNow({
|
|
rawUserName,
|
|
rawUserName,
|
|
- action: userName => {
|
|
|
|
- if (!(storyId in this.cache[userName].stories)) return reply('此动态不存在或已过期。');
|
|
|
|
- return this.workOnMedia([this.cache[userName].stories[storyId]], sender);
|
|
|
|
|
|
+ action: userId => {
|
|
|
|
+ if (!(storyId in this.cache[userId].stories)) return reply('此动态不存在或已过期。');
|
|
|
|
+ return this.workOnMedia([this.cache[userId].stories[storyId]], sender);
|
|
},
|
|
},
|
|
reply,
|
|
reply,
|
|
retryAction: () => sendStory(rawUserName, storyId, receiver),
|
|
retryAction: () => sendStory(rawUserName, storyId, receiver),
|
|
@@ -339,8 +340,9 @@ export default class {
|
|
const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
|
|
const sender = this.sendStories(`instagram stories for ${rawUserName}`, receiver);
|
|
workNow({
|
|
workNow({
|
|
rawUserName,
|
|
rawUserName,
|
|
- action: userName => {
|
|
|
|
- const storyItems = Object.values(this.cache[userName].stories)
|
|
|
|
|
|
+ action: userId => {
|
|
|
|
+ const userName = this.cache[userId].user.username;
|
|
|
|
+ const storyItems = Object.values(this.cache[userId].stories)
|
|
.sort((i1, i2) => -BigNumOps.compare(i2.pk, i1.pk)); // ascending!
|
|
.sort((i1, i2) => -BigNumOps.compare(i2.pk, i1.pk)); // ascending!
|
|
if (storyItems.length === 0) return reply(`当前用户 (@${userName}) 没有可用的 Instagram 限时动态。`);
|
|
if (storyItems.length === 0) return reply(`当前用户 (@${userName}) 没有可用的 Instagram 限时动态。`);
|
|
if (startIndex + 1 > storyItems.length) return reply('跳过数量到达或超过当前用户可用的限时动态数量。');
|
|
if (startIndex + 1 > storyItems.length) return reply('跳过数量到达或超过当前用户可用的限时动态数量。');
|
|
@@ -372,8 +374,8 @@ export default class {
|
|
|
|
|
|
public queryUser = (rawUserName: string) => {
|
|
public queryUser = (rawUserName: string) => {
|
|
const username = ScreenNameNormalizer.normalize(rawUserName).split(':')[0];
|
|
const username = ScreenNameNormalizer.normalize(rawUserName).split(':')[0];
|
|
- if (username in this.cache) {
|
|
|
|
- return Promise.resolve(`${username}:${this.cache[username].user.pk}`);
|
|
|
|
|
|
+ for (const {user} of Object.values(this.cache)) {
|
|
|
|
+ if (user.username === username) return Promise.resolve(`${username}:${user.pk}`);
|
|
}
|
|
}
|
|
return this.client.user.searchExact(username)
|
|
return this.client.user.searchExact(username)
|
|
.catch((error: IgClientError) => {
|
|
.catch((error: IgClientError) => {
|
|
@@ -384,7 +386,7 @@ export default class {
|
|
})
|
|
})
|
|
.then(user => {
|
|
.then(user => {
|
|
logger.info(`initialized cache item for user ${user.full_name} (@${username})`);
|
|
logger.info(`initialized cache item for user ${user.full_name} (@${username})`);
|
|
- this.cache[user.username] = {user, stories: {}, pullOrder: 0};
|
|
|
|
|
|
+ this.cache[user.pk] = {user, stories: {}, pullOrder: 0};
|
|
return `${user.username}:${user.pk}`;
|
|
return `${user.username}:${user.pk}`;
|
|
});
|
|
});
|
|
};
|
|
};
|
|
@@ -411,7 +413,7 @@ export default class {
|
|
};
|
|
};
|
|
|
|
|
|
private cache: {
|
|
private cache: {
|
|
- [userName: string]: {
|
|
|
|
|
|
+ [userId: string]: {
|
|
user: UserFeedResponseUser & ReelsMediaFeedResponseItem['user'],
|
|
user: UserFeedResponseUser & ReelsMediaFeedResponseItem['user'],
|
|
stories: {[storyId: string]: CachedMediaItem},
|
|
stories: {[storyId: string]: CachedMediaItem},
|
|
pullOrder: number, // one-based; -1: subscribed, awaiting shuffle; 0: not subscribed
|
|
pullOrder: number, // one-based; -1: subscribed, awaiting shuffle; 0: not subscribed
|
|
@@ -432,46 +434,42 @@ export default class {
|
|
private workForAll = () => {
|
|
private workForAll = () => {
|
|
if (this.isInactiveTime) return;
|
|
if (this.isInactiveTime) return;
|
|
logger.debug(`current cache: ${JSON.stringify(this.cache)}`);
|
|
logger.debug(`current cache: ${JSON.stringify(this.cache)}`);
|
|
- chainPromises(Object.entries(this.lock.threads).map(([feed, thread]) => {
|
|
|
|
|
|
+ chainPromises(Object.entries(this.lock.threads).map(([feed, thread]) => () => {
|
|
const id = thread.id;
|
|
const id = thread.id;
|
|
const userName = parseLink(feed).userName;
|
|
const userName = parseLink(feed).userName;
|
|
logger.debug(`preparing to add user @${userName} to next pull task...`);
|
|
logger.debug(`preparing to add user @${userName} to next pull task...`);
|
|
- return (map: {[key: number]: UserFeedResponseUser} = {}) => {
|
|
|
|
- if (userName in this.cache) {
|
|
|
|
- const item = this.cache[userName];
|
|
|
|
- if (item.pullOrder === 0) item.pullOrder = -1;
|
|
|
|
- return Promise.resolve(Object.assign(map, {[id]: item.user}));
|
|
|
|
- }
|
|
|
|
- return promisify(setTimeout)((Math.random() * 2 + 1) * 5000).then(() =>
|
|
|
|
- this.client.user.info(id).then(user => {
|
|
|
|
- logger.info(`initialized cache item for user ${user.full_name} (@${userName})`);
|
|
|
|
- this.cache[userName] = {user, stories: {}, pullOrder: -1};
|
|
|
|
- return Object.assign(map, {[id]: user});
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- };
|
|
|
|
|
|
+ if (id in this.cache) {
|
|
|
|
+ const item = this.cache[id];
|
|
|
|
+ if (item.pullOrder === 0) item.pullOrder = -1;
|
|
|
|
+ return Promise.resolve();
|
|
|
|
+ }
|
|
|
|
+ return promisify(setTimeout)((Math.random() * 2 + 1) * 5000).then(() =>
|
|
|
|
+ this.client.user.info(id).then(user => {
|
|
|
|
+ logger.info(`initialized cache item for user ${user.full_name} (@${user.username})`);
|
|
|
|
+ this.cache[id] = {user, stories: {}, pullOrder: -1};
|
|
|
|
+ })
|
|
|
|
+ );
|
|
}))
|
|
}))
|
|
- .then(idToUserMap => {
|
|
|
|
|
|
+ .then(() => {
|
|
const userIdCache = Object.values(this.cache).some(item => item.pullOrder < 0) ?
|
|
const userIdCache = Object.values(this.cache).some(item => item.pullOrder < 0) ?
|
|
- this.pullOrders = Arr.shuffle(Object.keys(idToUserMap)).map(Number) :
|
|
|
|
|
|
+ this.pullOrders = Arr.shuffle(Object.keys(this.cache)).map(Number) :
|
|
this.pullOrders;
|
|
this.pullOrders;
|
|
return chainPromises(
|
|
return chainPromises(
|
|
Arr.chunk(userIdCache, 20).map(userIds => () => {
|
|
Arr.chunk(userIdCache, 20).map(userIds => () => {
|
|
- const itemToUserName = (item: MediaItem) => idToUserMap[item.user.pk].username;
|
|
|
|
- logger.info(`pulling stories from users:${userIds.map(id => ` @${idToUserMap[id].username}`)}`);
|
|
|
|
|
|
+ logger.info(`pulling stories from users:${userIds.map(id => ` @${this.cache[id].user.username}`)}`);
|
|
return this.client.feed.reelsMedia({userIds}).items()
|
|
return this.client.feed.reelsMedia({userIds}).items()
|
|
.then(storyItems => Promise.all(storyItems
|
|
.then(storyItems => Promise.all(storyItems
|
|
- .filter(item => !(item.pk in this.cache[itemToUserName(item)].stories))
|
|
|
|
|
|
+ .filter(item => !(item.pk in this.cache[item.user.pk].stories))
|
|
.map(item => this.webshot(
|
|
.map(item => this.webshot(
|
|
- [{...item, user: this.cache[itemToUserName(item)].user}],
|
|
|
|
|
|
+ [{...item, user: this.cache[item.user.pk].user}],
|
|
(msgs: string, text: string, author: string) =>
|
|
(msgs: string, text: string, author: string) =>
|
|
- this.cache[itemToUserName(item)].stories[item.pk] = {pk: item.pk, msgs, text, author, original: item},
|
|
|
|
|
|
+ this.cache[item.user.pk].stories[item.pk] = {pk: item.pk, msgs, text, author, original: item},
|
|
this.webshotDelay
|
|
this.webshotDelay
|
|
))
|
|
))
|
|
))
|
|
))
|
|
.finally(() => Object.values(this.lock.threads).forEach(thread => {
|
|
.finally(() => Object.values(this.lock.threads).forEach(thread => {
|
|
if (userIds.includes(thread.id)) {
|
|
if (userIds.includes(thread.id)) {
|
|
- thread.updatedAt = (this.cache[idToUserMap[thread.id].username].updated = new Date()).toString();
|
|
|
|
|
|
+ thread.updatedAt = (this.cache[thread.id].updated = new Date()).toString();
|
|
}
|
|
}
|
|
})) as unknown as Promise<void>;
|
|
})) as unknown as Promise<void>;
|
|
}),
|
|
}),
|