Skip to content

Commit 3a38713

Browse files
committed
allow plugins to have useful constructors and access their own state
1 parent 91b0365 commit 3a38713

File tree

6 files changed

+74
-40
lines changed

6 files changed

+74
-40
lines changed

plugins/plugin.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
11
import * as puppeteer from "puppeteer";
2-
import * as instamancer from "..";
3-
import {TFullApiPost, TPost, TSearchResult, TSinglePost} from "..";
2+
import {Instagram, TFullApiPost, TPost, TSearchResult, TSinglePost} from "..";
43

54
export type DType = TPost | TSinglePost | TFullApiPost | TSearchResult;
65

7-
export interface IPlugin {
8-
constructionEvent?(this: instamancer.Instagram<DType>): void;
6+
export interface IPluginContext<Plugin, PostType> {
7+
plugin: Plugin;
8+
state: Instagram<PostType>;
9+
}
10+
11+
export interface IPlugin<PostType> {
12+
constructionEvent?(this: IPluginContext<IPlugin<PostType>, PostType>): void;
913

1014
requestEvent?(
11-
this: instamancer.Instagram<DType>,
15+
this: IPluginContext<IPlugin<PostType>, PostType>,
1216
req: puppeteer.Request,
1317
overrides: puppeteer.Overrides,
1418
): void;
1519

1620
responseEvent?(
17-
this: instamancer.Instagram<DType>,
21+
this: IPluginContext<IPlugin<PostType>, PostType>,
1822
res: puppeteer.Response,
19-
data: DType,
23+
data: {[key: string]: any},
2024
): void;
2125

22-
postPageEvent?(this: instamancer.Instagram<DType>, data: DType): void;
26+
postPageEvent?(
27+
this: IPluginContext<IPlugin<PostType>, PostType>,
28+
data: PostType,
29+
): void;
2330

24-
graftingEvent?(this: instamancer.Instagram<DType>): void;
31+
graftingEvent?(this: IPluginContext<IPlugin<PostType>, PostType>): void;
2532
}

plugins/plugins/largeFirst.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import {Overrides, Request} from "puppeteer";
22
import * as querystring from "querystring";
33
import {format as urlFormat, parse as urlParse} from "url";
4-
import {Instagram} from "../../src/api/instagram";
5-
import {DType, IPlugin} from "../plugin";
4+
import {IPlugin, IPluginContext} from "../plugin";
65

7-
export class LargeFirst implements IPlugin {
8-
public constructionEvent(this: Instagram<DType>): void {
9-
this.jumpSize = 150;
6+
export class LargeFirst<PostType> implements IPlugin<PostType> {
7+
public constructionEvent(
8+
this: IPluginContext<IPlugin<PostType>, PostType>,
9+
): void {
10+
this.state.jumpSize = 150;
1011
}
1112

1213
public requestEvent(req: Request, overrides: Overrides): void {

src/api/api.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import {Type} from "io-ts";
22
import * as winston from "winston";
3-
import {IPlugin} from "../../plugins/plugin";
3+
import {DType, IPlugin} from "../../plugins";
44
import {Instagram} from "./instagram";
5-
import {ISearchOptions, Search} from "./search";
5+
import {
6+
ISearchOptions,
7+
ISearchOptionsPlugins,
8+
Search,
9+
TSearchResult,
10+
} from "./search";
611
import {
712
FullApiPost,
813
Post as PostValidator,
@@ -51,9 +56,6 @@ export interface IOptionsCommon {
5156

5257
// Custom io-ts validator
5358
validator?: Type<unknown>;
54-
55-
// Custom plugins
56-
plugins?: IPlugin[];
5759
}
5860

5961
export interface IOptionsFullApi extends IOptionsCommon {
@@ -64,7 +66,19 @@ export interface IOptionsRegular extends IOptionsCommon {
6466
fullAPI?: false;
6567
}
6668

67-
export type IOptions = IOptionsFullApi | IOptionsRegular;
69+
export interface IOptionsFullApiPlugins<PostType> extends IOptionsFullApi {
70+
plugins?: Array<IPlugin<PostType>>;
71+
}
72+
73+
export interface IOptionsRegularPlugins<PostType> extends IOptionsRegular {
74+
plugins?: Array<IPlugin<PostType>>;
75+
}
76+
77+
export type IOptions =
78+
| IOptionsFullApi
79+
| IOptionsRegular
80+
| IOptionsFullApiPlugins<DType>
81+
| IOptionsRegularPlugins<DType>;
6882

6983
/**
7084
* An Instagram post API wrapper
@@ -104,19 +118,20 @@ export type InstagramFullPostClass = Hashtag<TFullApiPost> | User<TFullApiPost>;
104118
export function createApi(
105119
type: "search",
106120
query: string,
107-
options?: ISearchOptions,
121+
options?: ISearchOptions | ISearchOptionsPlugins<TSearchResult>,
108122
): Search;
109123
export function createApi(type: "post", id: string[], options?: IOptions): Post;
110124
export function createApi(
111125
type: "hashtag" | "user",
112126
id: string,
113-
options?: IOptionsRegular,
127+
options?: IOptionsRegular | IOptionsRegularPlugins<InstagramPostClass>,
114128
): InstagramPostClass;
115129
export function createApi(
116130
type: "hashtag" | "user",
117131
id: string,
118-
options?: IOptionsFullApi,
132+
options?: IOptionsFullApi | IOptionsFullApiPlugins<InstagramFullPostClass>,
119133
): InstagramFullPostClass;
134+
120135
export function createApi(
121136
type: "hashtag" | "user" | "post" | "search",
122137
id: string | string[],

src/api/instagram.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from "puppeteer";
1818
import * as winston from "winston";
1919
import {IPlugin} from "../../plugins";
20+
import {IPluginContext} from "../../plugins/plugin";
2021
import {IOptions} from "./api";
2122
import {PostIdSet} from "./postIdSet";
2223

@@ -191,7 +192,7 @@ export class Instagram<PostType> extends EventEmitter {
191192
this.executablePath = options.executablePath;
192193
this.validator = options.validator || validator;
193194

194-
this.addPlugins(options.plugins);
195+
this.addPlugins(options["plugins"]);
195196
this.emit("construction");
196197
}
197198

@@ -289,6 +290,13 @@ export class Instagram<PostType> extends EventEmitter {
289290
}
290291
}
291292

293+
/**
294+
* Match the url to the url used in API requests
295+
*/
296+
public matchURL(url: string) {
297+
return url.startsWith(this.catchURL) && !url.includes("include_reel");
298+
}
299+
292300
/**
293301
* Close the page and browser
294302
*/
@@ -461,13 +469,6 @@ export class Instagram<PostType> extends EventEmitter {
461469
}
462470
}
463471

464-
/**
465-
* Match the url to the url used in API requests
466-
*/
467-
protected matchURL(url: string) {
468-
return url.startsWith(this.catchURL) && !url.includes("include_reel");
469-
}
470-
471472
/**
472473
* Open a post in a new page, then extract its metadata
473474
*/
@@ -861,7 +862,7 @@ export class Instagram<PostType> extends EventEmitter {
861862
}
862863
}
863864

864-
private addPlugins(plugins: IPlugin[]) {
865+
private addPlugins(plugins: Array<IPlugin<PostType>>) {
865866
if (!plugins) {
866867
return;
867868
}
@@ -878,7 +879,12 @@ export class Instagram<PostType> extends EventEmitter {
878879
for (const event of events) {
879880
const pluginEvent = plugin[event + "Event"];
880881
if (pluginEvent) {
881-
this.on(event, pluginEvent);
882+
const context: IPluginContext<typeof plugin, PostType> = {
883+
plugin,
884+
state: this,
885+
};
886+
887+
this.on(event, pluginEvent.bind(context));
882888
}
883889
}
884890
}

src/api/search.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as t from "io-ts";
22
import {excess} from "io-ts-excess";
3+
import {IPlugin} from "../../plugins";
34
import {IOptions} from "./api";
45
import {Instagram} from "./instagram";
56

@@ -82,10 +83,14 @@ export type ISearchOptions = Pick<
8283
>
8384
>;
8485

86+
export interface ISearchOptionsPlugins<PostType> extends ISearchOptions {
87+
plugins?: Array<IPlugin<PostType>>;
88+
}
89+
8590
export class Search extends Instagram<TSearchResult> {
8691
public readonly catchURL = "https://www.instagram.com/web/";
8792
private searchResult: TSearchResult;
88-
private searchQuery: string;
93+
private readonly searchQuery: string;
8994

9095
constructor(query: string, options: ISearchOptions = {}) {
9196
super(
@@ -118,7 +123,7 @@ export class Search extends Instagram<TSearchResult> {
118123
}
119124
}
120125

121-
protected matchURL(url: string) {
126+
public matchURL(url: string) {
122127
return url.startsWith(this.catchURL);
123128
}
124129

tests/test.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import * as t from "io-ts";
22
import {Overrides, Request} from "puppeteer";
33
import * as winston from "winston";
4-
import {createApi, DType, Instagram, IPlugin} from "..";
4+
import {createApi, IPlugin} from "..";
55
import {plugins} from "..";
6+
import {IPluginContext} from "../plugins/plugin";
67
import {IOptions, IOptionsFullApi} from "../src/api/api";
78
import {FakePage, IFakePageOptions} from "./__fixtures__/FakePage";
89
import {QuickGraft} from "./__fixtures__/QuickGraft";
@@ -531,14 +532,13 @@ describe("Search", () => {
531532

532533
test("Search should fire only one network request", async () => {
533534
const searchRequestsSpy = jest.fn();
534-
class RequestCounter implements IPlugin {
535+
class RequestCounter<PostType> implements IPlugin<PostType> {
535536
public requestEvent(
536-
this: Instagram<DType>,
537+
this: IPluginContext<IPlugin<PostType>, PostType>,
537538
req: Request,
538539
overrides: Overrides,
539540
): void {
540-
// @ts-ignore
541-
if (this.matchURL(req.url())) {
541+
if (this.state.matchURL(req.url())) {
542542
searchRequestsSpy();
543543
}
544544
}

0 commit comments

Comments
 (0)