├── wsgi.py ├── static ├── twitter-icon.png └── picard-facepalm.jpg ├── twintelligence.ini ├── deployment ├── nginx │ └── twintelligence └── systemd │ └── twintelligence.service ├── .gitignore ├── LICENSE.md ├── templates ├── contact.html ├── fail.html ├── about.html ├── index.html └── report.html ├── README.md └── twintelligence.py /wsgi.py: -------------------------------------------------------------------------------- 1 | from twintelligence import app 2 | 3 | if __name__ == "__main__": 4 | app.run() 5 | -------------------------------------------------------------------------------- /static/twitter-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jipegit/Twintelligence/HEAD/static/twitter-icon.png -------------------------------------------------------------------------------- /static/picard-facepalm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jipegit/Twintelligence/HEAD/static/picard-facepalm.jpg -------------------------------------------------------------------------------- /twintelligence.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | module = wsgi:app 3 | 4 | master = true 5 | processes = 5 6 | 7 | socket = twintelligence.sock 8 | chmod-socket = 660 9 | vacuum = true 10 | 11 | die-on-term = true 12 | -------------------------------------------------------------------------------- /deployment/nginx/twintelligence: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name twintelligence.io; 4 | 5 | location / { 6 | include uwsgi_params; 7 | uwsgi_pass unix:/home/dev/Twintelligence/twintelligence.sock; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /deployment/systemd/twintelligence.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=uWSGI instance to serve Twintelligence 3 | After=network.target 4 | 5 | [Service] 6 | User=dev 7 | Group=www-data 8 | WorkingDirectory=/home/dev/Twintelligence 9 | Environment="PATH=/home/dev/twintelligence-env/bin" 10 | ExecStart=/home/dev/twintelligence-env/bin/uwsgi --ini twintelligence.ini 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | *.txt 30 | #*.html 31 | *.token 32 | # Translations 33 | *.mo 34 | 35 | # Mr Developer 36 | .mr.developer.cfg 37 | .project 38 | .pydevproject 39 | 40 | 41 | # folders 42 | env-* 43 | libs 44 | 45 | 46 | # MAC OS X stuff 47 | .DS_Store 48 | .DS_Store? 49 | ._* 50 | .Spotlight-V100 51 | .Trashes 52 | Icon? 53 | ehthumbs.db 54 | Thumbs.db 55 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Twintelligence 2 | Copyright (C) 2013 Jean-Philippe Teissier 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program. If not, see . -------------------------------------------------------------------------------- /templates/contact.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Twintelligence 5 | 6 | 7 | 8 | 9 | 10 | 11 | 22 | 23 | 24 |
25 |
26 | 31 |

Twintelligence

32 |
33 |
34 |

Contact

35 |

@Jipe_

36 |
37 |
38 | 39 | -------------------------------------------------------------------------------- /templates/fail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Twintelligence 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 23 | 24 | 25 |
26 |
27 |

Twintelligence

28 |
29 |
30 |

Oops!

31 |
32 | {{ error }} 33 |
34 | FAIL 35 |
36 |
37 | 38 | -------------------------------------------------------------------------------- /templates/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Twintelligence 5 | 6 | 7 | 8 | 9 | 10 | 11 | 23 | 24 | 25 |
26 |
27 | 32 |

Twintelligence

33 |
34 |
35 |

About

36 |

Twintelligence is an OSINT tool. Beware of the traces you leave on the Internet. This is an alpha software.

37 |
38 |
39 | 40 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Twintelligence 5 | 6 | 7 | 8 | 9 | 10 | 11 | 23 | 24 | 25 |
26 |
27 | 32 |

Twintelligence

33 |
34 |
35 |

Twintelligence Report

36 |

Enter a Twitter @UserName to generate the report. 37 | This is an alpha software, don't push it to hard ;)

38 |
39 |
40 | 41 | 42 |
43 |
44 | 45 | 46 |
47 | 48 |
49 |
50 |
51 | 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Twintelligence 2 | 3 | Twintelligence is a free Twitter OSINT tool 4 | 5 | ![Maintenance](https://img.shields.io/badge/Maintained%3F-no-red.svg) 6 | ![No Maintenance Intended](http://unmaintained.tech/badge.svg) 7 | 8 | ## Author 9 | 10 | Jean-Philippe Teissier - @Jipe_ 11 | 12 | ## Development status 13 | 14 | **Twintelligence is no longer maintained** 15 | 16 | ## How to install 17 | 18 | Copy all files from Github 19 | 20 | ## Dependencies 21 | 22 | * pip install python-twitter 23 | * pip install flask 24 | 25 | If you want to run it with Nginx and uwsgi 26 | 27 | * apt-get install nginx uwsgi uwsgi-plugin-python 28 | 29 | ## Setup 30 | 31 | 1. Create an application. See https://dev.twitter.com/ 32 | 33 | 2. Go to your application's settings -> OAuth settings and copy/paste your "Consumer key" and your "Consumer key" to the YOUR_APP_CONSUMER_KEY and YOUR_APP_CONSUMER_SECRET variables in the source code 34 | 35 | 3. Go to your application's settings -> Application Type, and change the Access parameter to "Read, Write and Access direct messages". Update the settings 36 | 37 | 3. Go back to your application's details and click on "Recreate my access token". Copy/paste your "Access token" and your "Access token secret" to the YOUR_ACCESS_TOKEN_SECRET and YOUR_ACCESS_TOKEN in the source code. Alternatively you can pass them as arguments with -k/--accesstokenkey and -s/accesstokensecret 38 | 39 | ## How to run 40 | 41 | python twintelligence.py 42 | 43 | ## Changelog 44 | 45 | ### 0.2 46 | * python-twitter & API model update 47 | 48 | ### 0.1 49 | * Initial alpha release 50 | 51 | ## License 52 | 53 | Twintelligence 54 | Copyright (C) 2013-2016 Jean-Philippe Teissier 55 | 56 | This program is free software: you can redistribute it and/or modify 57 | it under the terms of the GNU General Public License as published by 58 | the Free Software Foundation, either version 3 of the License, or 59 | (at your option) any later version. 60 | 61 | This program is distributed in the hope that it will be useful, 62 | but WITHOUT ANY WARRANTY; without even the implied warranty of 63 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 64 | GNU General Public License for more details. 65 | 66 | You should have received a copy of the GNU General Public License 67 | along with this program. If not, see . 68 | -------------------------------------------------------------------------------- /twintelligence.py: -------------------------------------------------------------------------------- 1 | # 2 | # Twintelligence 3 | # 4 | # Twintelligence is a free Twitter OSINT tool 5 | # 6 | # Author: @Jipe_ 7 | # 8 | 9 | import twitter 10 | from datetime import * 11 | from flask import Flask, render_template, redirect, request 12 | 13 | YOUR_APP_CONSUMER_KEY = "" 14 | YOUR_APP_CONSUMER_SECRET = "" 15 | 16 | YOUR_ACCESS_TOKEN = "" 17 | YOUR_ACCESS_TOKEN_SECRET = "" 18 | 19 | app = Flask(__name__) 20 | 21 | 22 | class Data(object): 23 | 24 | def __init__(self, g, l, h): 25 | self.g = g 26 | self.l = l 27 | self.h = h 28 | 29 | 30 | def getstatuses(twapi, userid, twnumber): 31 | """ Get the list of Tweets""" 32 | 33 | totalitems = 0 34 | items = None 35 | gpscoordinates = [] 36 | langs = {} 37 | hours = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 38 | maxid = 0 39 | 40 | print("[D] Retrieving the list of tweets (it might take a while...)") 41 | 42 | try: 43 | items = twapi.GetUserTimeline(user_id=userid, count=200) # Can't request more than 200 items at a time 44 | except twitter.TwitterError as e: 45 | print(u"[-] ERROR: (" + e[0] + ")") 46 | return(Data(0, 0, 0)) 47 | 48 | while len(items) > 0 and totalitems < twnumber: 49 | if maxid == items[-1].id: 50 | break 51 | maxid = items[-1].id 52 | for item in items: 53 | if totalitems >= twnumber: 54 | break 55 | 56 | if item.coordinates: 57 | gpscoordinates.append({'created_at' : item.created_at, 'lat' : item.coordinates['coordinates'][1], 'lng' : item.coordinates['coordinates'][0]}) 58 | 59 | if item.lang in langs: 60 | langs[item.lang] += 1 61 | else: 62 | langs[item.lang] = 1 63 | 64 | hours[datetime.strptime(item.created_at, "%a %b %d %H:%M:%S +0000 %Y").hour] += 1 65 | 66 | totalitems += 1 67 | print("[D] [" + str(totalitems) + "] - "+ str(item.id) + " (" + item.created_at + ") - lang: " + item.lang + " added") 68 | # print(item) 69 | 70 | try: 71 | items = twapi.GetUserTimeline(user_id=userid, count=200, max_id=maxid) 72 | except twitter.TwitterError as e: 73 | print(u"[-] ERROR: (" + e[0] + ")") 74 | return(Data(0, 0, 0)) 75 | 76 | print("[D] Got " + str(totalitems) + " tweets") 77 | 78 | print gpscoordinates 79 | 80 | langscountries = [] 81 | langsnumbers = [] 82 | langsdata = [] 83 | 84 | for key in langs.keys(): 85 | langscountries.append(key) 86 | langsnumbers.append(langs[key]) 87 | 88 | langsdata.append(langscountries) 89 | langsdata.append(langsnumbers) 90 | 91 | return(Data(gpscoordinates, langsdata, hours)) 92 | 93 | 94 | @app.route("/") 95 | def index(): 96 | return render_template("index.html") 97 | 98 | 99 | @app.route("/contact") 100 | def contact(): 101 | return render_template("contact.html") 102 | 103 | 104 | @app.route("/about") 105 | def about(): 106 | return render_template("about.html") 107 | 108 | 109 | @app.route("/fail") 110 | def fail(): 111 | return render_template("fail.html") 112 | 113 | 114 | @app.route("/report", methods=['GET', 'POST']) 115 | def report(): 116 | try: 117 | if request.method == 'POST': 118 | twapi = twitter.Api(consumer_key=YOUR_APP_CONSUMER_KEY, 119 | consumer_secret=YOUR_APP_CONSUMER_SECRET, 120 | access_token_key=YOUR_ACCESS_TOKEN, 121 | access_token_secret=YOUR_ACCESS_TOKEN_SECRET) 122 | 123 | target_user = twapi.GetUser(screen_name=request.form['screen_name']) 124 | userid = target_user.id 125 | 126 | if request.form['nbtweets'] != "": 127 | nbtweets = int(request.form['nbtweets']) 128 | if nbtweets > 2000: 129 | nbtweets = 2000 130 | else: 131 | nbtweets = 1000 132 | 133 | print('[D] Trying to get [' + str(nbtweets) + '] tweets') 134 | 135 | userdetails = {} 136 | userdetails['screen_name'] = target_user.screen_name 137 | userdetails['name'] = target_user.name 138 | userdetails['created_at'] = target_user.created_at 139 | userdetails['location'] = target_user.location 140 | userdetails['utcoffset'] = str(target_user.utc_offset) 141 | userdetails['tz'] = target_user.time_zone 142 | userdetails['lang'] = target_user.lang 143 | userdetails['nbtweets'] = nbtweets 144 | 145 | firstfollowers = [] 146 | firstfriends = [] 147 | 148 | print('[D] Trying to get the followers') 149 | followersid = twapi.GetFollowerIDs(user_id=userid) 150 | if len(followersid) <= 20: 151 | firstfollowersid = followersid 152 | else: 153 | firstfollowersid = followersid[-21:-1] 154 | 155 | #print('[D] Got ' + str(len(followers)) + ' followers') 156 | 157 | print('[D] Trying to get the friends') 158 | 159 | friendsid = twapi.GetFriendIDs(user_id=userid) 160 | if len(friendsid) <= 20: 161 | firstfriendsid = friendsid 162 | else: 163 | firstfriendsid = friendsid[-21:-1] 164 | 165 | if firstfollowersid: 166 | firstfollowers = twapi.UsersLookup(user_id=firstfollowersid) 167 | 168 | if firstfriendsid: 169 | firstfriends = twapi.UsersLookup(user_id=firstfriendsid) 170 | 171 | userdetails['firstfollowers'] = reversed([x.screen_name for x in firstfollowers]) 172 | userdetails['firstfriends'] = reversed([x.screen_name for x in firstfriends]) 173 | 174 | # print userdetails['firstfollowers'] 175 | # print userdetails['firstfriends'] 176 | 177 | # print('[D] Joined Friends/Followers') 178 | # for f in fff: 179 | # print('[D] ' + f.screen_name) 180 | 181 | print('[D] Trying to get [' + str(nbtweets) + '] tweets') 182 | returneddata = getstatuses(twapi, userid, nbtweets) 183 | 184 | return render_template("report.html", 185 | userdetails = userdetails, 186 | gpsdata = returneddata.g, 187 | langsbarchartdata = returneddata.l, 188 | hoursbarchartdata = returneddata.h) 189 | else: 190 | return redirect("/") 191 | except twitter.TwitterError as e: 192 | print e # [0]["message"] 193 | return render_template("fail.html", 194 | error = e[0]) 195 | 196 | 197 | if __name__ == "__main__": 198 | app.run() 199 | -------------------------------------------------------------------------------- /templates/report.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Twintelligence Report {{ userdetails.screen_name }} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 89 | 106 | 107 | 108 |
109 | 114 |

Twintelligence Report

115 |
116 |
117 |
118 | {{ userdetails.screen_name }} - {{ userdetails.name }}
119 | Created at {{ userdetails.created_at }}
120 | Location: {{ userdetails.location }} - Language: {{ userdetails.lang }}
121 | UTC Offset: {{ userdetails.utcoffset }} - TimeZone: {{ userdetails.tz }}
122 | {{ userdetails.nbtweets }} tweets analyzed. The above information is declarative. 123 |
124 |
125 |
126 |
>
127 |
128 |
129 |
130 |
131 |
132 |

133 | 134 | Show GPS Coordinates 135 | 136 |

137 |
138 |
139 |
140 | {% for gps_position in gpsdata %} {{ gps_position.created_at }}: {{ gps_position.lat }}, {{ gps_position.lng }}
{% endfor %} 141 |
142 |
143 |
144 |
145 |
146 |
147 | 148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | {% for follower in userdetails.firstfollowers %} {% endfor %} 169 | 170 |
First followers (Top 20)
@{{ follower }}
171 |
172 |
173 |
174 |
175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | {% for friend in userdetails.firstfriends %} {% endfor %} 183 | 184 |
First friends (Top 20)
@{{ friend }}
185 |
186 |
187 |
188 | 189 | --------------------------------------------------------------------------------