src/actions/Search.ts   A
last analyzed

Complexity

Total Complexity 18
Complexity/F 3.6

Size

Lines of Code 125
Function Count 5

Duplication

Duplicated Lines 0
Ratio 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 103
dl 0
loc 125
ccs 57
cts 57
cp 1
rs 10
c 0
b 0
f 0
wmc 18
mnd 13
bc 13
fnc 5
bpm 2.6
cpm 3.6
noi 0

5 Functions

Rating   Name   Duplication   Size   Complexity  
A Search.toTweets 0 16 3
A Search.hasAlreadyReTweeted 0 14 3
B Search.run 0 29 6
A Search.searchRetweetedTweets 0 26 3
A Search.search 0 20 3
1
import Twit from 'twit';
2 3
import Helper from '../Helper';
3 3
import Tweet from '../entities/Tweet';
4 3
import {AbstractAction, runArgs} from './Action';
5
6
interface runSearchArgs extends runArgs {
7
    searchParams: Twit.Params
8
}
9
10 3
export default class Search extends AbstractAction {
11
    async run(args: runSearchArgs): Promise<void> {
12 9
        const tweets = await this.search(args.searchParams);
13
14 6
        if (!Helper.objectExists(tweets) || tweets.length === 0) {
15 1
            this.debug('Found no tweets.');
16 1
            return;
17
        }
18
19 5
        this.debug(`Found '${tweets.length}' tweet(s).`);
20
21 5
        const retweetedTweets = await this.searchRetweetedTweets();
22
23 3
        let count = 0;
24 3
        for (const tweet of tweets) {
25 4
            count++;
26
27 4
            if (!tweet.isReTweetable()) {
28 1
                this.debug(`${count}. Tweet with id: '${tweet.rawTweet.id_str}' is not retweetable because: ${tweet.retweetError}.`);
29 1
                continue;
30
            }
31
32 3
            this.debug(`${count}. Tweet with id: '${tweet.rawTweet.id_str}' may be retweeted!`);
33
34 3
            if (this.hasAlreadyReTweeted(retweetedTweets, tweet)) {
35 1
                continue;
36
            }
37
38 2
            if (args.onSuccess) {
39 1
                await args.onSuccess(tweet);
40
            }
41
        }
42
    }
43
44
    private async search(searchParams: Twit.Params): Promise<Tweet[]> {
45 9
        this.debug('Searching recent tweets with the following params ...');
46 9
        this.debug(searchParams);
47
48 9
        const result = await this.twit.get('search/tweets', searchParams);
49
50 8
        if (result.resp.statusCode === 200 && Helper.objectExists(result.data)) {
51 6
            this.debug('Search successfully completed!');
52
53 6
            const statuses = (result.data as Twit.Twitter.SearchResults).statuses;
54
55 6
            return this.toTweets(statuses);
56
        } else {
57 2
            this.debug('Cannot search recent tweets.');
58 2
            if (Helper.objectExists(result.resp.statusMessage)) {
59 1
                this.debug(result.resp.statusMessage);
60
            }
61
62 2
            throw new Error('Cannot search recent tweets');
63
        }
64
    }
65
66
    private async searchRetweetedTweets(): Promise<Tweet[]> {
67 5
        this.debug(`Searching recent retweets by user: '${this.config.screenName}' ...`);
68
69 5
        const result = await this.twit.get('statuses/user_timeline', {
70
            screen_name: this.config.screenName,
71
            // max allowed is 200
72
            count: 200,
73
            exclude_replies: true,
74
            include_rts: true
75
        });
76
77 5
        if (result.resp.statusCode === 200 && Helper.objectExists(result.data)) {
78 3
            const recentTweets = result.data as Twit.Twitter.Status[];
79
80 3
            const retweets = this.toTweets(recentTweets, true);
81 3
            this.debug(`Found '${retweets.length}' retweet(s).`);
82
83 3
            return retweets;
84
        } else {
85 2
            this.debug('Cannot search recent retweets.');
86 2
            if (Helper.objectExists(result.resp.statusMessage)) {
87 1
                this.debug(result.resp.statusMessage);
88
            }
89
90 2
            throw new Error('Cannot search recent retweets');
91
        }
92
    }
93
94
    private hasAlreadyReTweeted(retweetedTweets: Tweet[], tweet: Tweet): boolean {
95 3
        this.debug(`Checking to see if tweet with id: '${tweet.rawTweet.id_str}' has been already retweeted ...`);
96
97 3
        for (const recentTweet of retweetedTweets) {
98 4
            if (recentTweet.rawTweet.retweeted_status && recentTweet.rawTweet.retweeted_status.id_str === tweet.rawTweet.id_str) {
99 1
                this.debug(`Tweet with id: '${tweet.rawTweet.id_str}' has been already tweeted.`);
100 1
                return true;
101
            }
102
        }
103
104 2
        this.debug(`Tweet with id: '${tweet.rawTweet.id_str}' has NOT been tweeted.`);
105
106 2
        return false;
107
    }
108
109
    private toTweets(statuses: Twit.Twitter.Status[], onlyRetweets = false): Tweet[] {
110 9
        const tweets: Tweet[] = [];
111
112
        let tweet: Tweet;
113 9
        for (const status of statuses) {
114 9
            tweet = new Tweet(status, this.config.tweetConfig);
115
116 9
            if (onlyRetweets && !tweet.isRetweet()) {
117 1
                continue;
118
            }
119
120 8
            tweets.push(tweet);
121
        }
122
123 9
        return tweets;
124
    }
125
}