mirror of
https://github.com/arnaucube/echo-botnet.git
synced 2026-02-07 03:16:42 +01:00
works
This commit is contained in:
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
|
||||||
|
botsConfig.json
|
||||||
|
build/botsConfig.json
|
||||||
|
repliesConfig.json
|
||||||
|
keywordsConfig.json
|
||||||
|
temp
|
||||||
|
text.txt
|
||||||
40
README.md
40
README.md
@@ -1,2 +1,40 @@
|
|||||||
# echo-botnet
|
# echo-botnet [](https://goreportcard.com/report/github.com/arnaucode/echo-botnet)
|
||||||
A twitter botnet with autonomous bots replying tweets with pre-configured replies
|
A twitter botnet with autonomous bots replying tweets with pre-configured replies
|
||||||
|
|
||||||
|
|
||||||
|
Echo (Ēkhō): https://en.wikipedia.org/wiki/Echo_(mythology)#
|
||||||
|
|
||||||
|
|
||||||
|
Needs the files:
|
||||||
|
```
|
||||||
|
botsConfig.json --> contains the tokens of the bots
|
||||||
|
keywordsConfig.json --> contains the keywords to track from Twitter API
|
||||||
|
repliesConfig.json --> contains the replies
|
||||||
|
```
|
||||||
|
|
||||||
|
configuration file example (botsConfig.json):
|
||||||
|
```
|
||||||
|
[{
|
||||||
|
"title": "account1",
|
||||||
|
"consumer_key": "xxxxxxxxxxxxx",
|
||||||
|
"consumer_secret": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_key": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_secret": "xxxxxxxxxxxxx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "account2",
|
||||||
|
"consumer_key": "xxxxxxxxxxxxx",
|
||||||
|
"consumer_secret": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_key": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_secret": "xxxxxxxxxxxxx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "account3",
|
||||||
|
"consumer_key": "xxxxxxxxxxxxx",
|
||||||
|
"consumer_secret": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_key": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_secret": "xxxxxxxxxxxxx"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
```
|
||||||
|
|||||||
22
build/botsConfigDEMO.json
Normal file
22
build/botsConfigDEMO.json
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
[{
|
||||||
|
"title": "account1",
|
||||||
|
"consumer_key": "xxxxxxxxxxxxx",
|
||||||
|
"consumer_secret": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_key": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_secret": "xxxxxxxxxxxxx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "account2",
|
||||||
|
"consumer_key": "xxxxxxxxxxxxx",
|
||||||
|
"consumer_secret": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_key": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_secret": "xxxxxxxxxxxxx"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "account3",
|
||||||
|
"consumer_key": "xxxxxxxxxxxxx",
|
||||||
|
"consumer_secret": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_key": "xxxxxxxxxxxxx",
|
||||||
|
"access_token_secret": "xxxxxxxxxxxxx"
|
||||||
|
}
|
||||||
|
]
|
||||||
BIN
build/echo-botnet
Executable file
BIN
build/echo-botnet
Executable file
Binary file not shown.
6
build/keywordsConfigDEMO.json
Normal file
6
build/keywordsConfigDEMO.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[
|
||||||
|
"word1",
|
||||||
|
"word2",
|
||||||
|
"wordToTrack3",
|
||||||
|
"word4"
|
||||||
|
]
|
||||||
7
build/repliesConfigDEMO.json
Normal file
7
build/repliesConfigDEMO.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[
|
||||||
|
"Reply message 1",
|
||||||
|
"Reply message 2",
|
||||||
|
"Reply message 3",
|
||||||
|
"Hey wassup friend!",
|
||||||
|
"You like the botnet?"
|
||||||
|
]
|
||||||
57
color.go
Normal file
57
color.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
//Color struct, defines the color
|
||||||
|
type Color struct{}
|
||||||
|
|
||||||
|
var c Color
|
||||||
|
|
||||||
|
//DarkGray color
|
||||||
|
func (c Color) DarkGray(t string) {
|
||||||
|
fmt.Print("\x1b[30;1m") //dark gray
|
||||||
|
fmt.Println(t)
|
||||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||||
|
}
|
||||||
|
|
||||||
|
//Red color
|
||||||
|
func (c Color) Red(t string) {
|
||||||
|
fmt.Print("\x1b[31;1m") //red
|
||||||
|
fmt.Println(t)
|
||||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||||
|
}
|
||||||
|
|
||||||
|
//Green color
|
||||||
|
func (c Color) Green(t string) {
|
||||||
|
fmt.Print("\x1b[32;1m") //green
|
||||||
|
fmt.Println(t)
|
||||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||||
|
}
|
||||||
|
|
||||||
|
//Yellow color
|
||||||
|
func (c Color) Yellow(t string) {
|
||||||
|
fmt.Print("\x1b[33;1m") //yellow
|
||||||
|
fmt.Println(t)
|
||||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||||
|
}
|
||||||
|
|
||||||
|
//Blue color
|
||||||
|
func (c Color) Blue(t string) {
|
||||||
|
fmt.Print("\x1b[34;1m") //blue
|
||||||
|
fmt.Println(t)
|
||||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||||
|
}
|
||||||
|
|
||||||
|
//Purple color
|
||||||
|
func (c Color) Purple(t string) {
|
||||||
|
fmt.Print("\x1b[35;1m") //purple
|
||||||
|
fmt.Println(t)
|
||||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||||
|
}
|
||||||
|
|
||||||
|
//Cyan color
|
||||||
|
func (c Color) Cyan(t string) {
|
||||||
|
fmt.Print("\x1b[36;1m") //cyan
|
||||||
|
fmt.Println(t)
|
||||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||||
|
}
|
||||||
BIN
echo-botnet
Executable file
BIN
echo-botnet
Executable file
Binary file not shown.
28
main.go
Normal file
28
main.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const version = "0.1-dev"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c.Yellow("echo-botnet")
|
||||||
|
fmt.Println("---------------")
|
||||||
|
c.Cyan("echo-botnet initialized")
|
||||||
|
c.Purple("https://github.com/arnaucode/echo-botnet")
|
||||||
|
fmt.Println("version " + version)
|
||||||
|
|
||||||
|
keywords := readKeywordsConfig()
|
||||||
|
c.Cyan("keywords configured: ")
|
||||||
|
fmt.Println(keywords)
|
||||||
|
replies := readRepliesConfig()
|
||||||
|
c.Cyan("replies configured: ")
|
||||||
|
fmt.Println(replies)
|
||||||
|
|
||||||
|
fmt.Println("Reading botsConfig.json file")
|
||||||
|
botnet := readConfigTokensAndConnect()
|
||||||
|
c.Cyan("[list of configured bots]:")
|
||||||
|
for _, v := range botnet.ScreenNames {
|
||||||
|
c.Cyan(v)
|
||||||
|
}
|
||||||
|
streamTweets(botnet, keywords, replies)
|
||||||
|
}
|
||||||
54
readConfigTokensAndConnect.go
Normal file
54
readConfigTokensAndConnect.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/dghubble/go-twitter/twitter"
|
||||||
|
"github.com/dghubble/oauth1"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Config stores the data from json botsConfig.json file
|
||||||
|
type Config struct {
|
||||||
|
Title string `json:"title"`
|
||||||
|
Consumer_key string `json:"consumer_key"`
|
||||||
|
Consumer_secret string `json:"consumer_secret"`
|
||||||
|
Access_token_key string `json:"access_token_key"`
|
||||||
|
Access_token_secret string `json:"access_token_secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//Botnet stores each bot configured
|
||||||
|
type Botnet struct {
|
||||||
|
ScreenNames []string
|
||||||
|
Clients []*twitter.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func readConfigTokensAndConnect() (botnet Botnet) {
|
||||||
|
var config []Config
|
||||||
|
var clients []*twitter.Client
|
||||||
|
|
||||||
|
file, e := ioutil.ReadFile("botsConfig.json")
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println("error:", e)
|
||||||
|
}
|
||||||
|
content := string(file)
|
||||||
|
json.Unmarshal([]byte(content), &config)
|
||||||
|
fmt.Println("botnetConfig.json read comlete")
|
||||||
|
|
||||||
|
fmt.Print("connecting to twitter api --> ")
|
||||||
|
for i := 0; i < len(config); i++ {
|
||||||
|
configu := oauth1.NewConfig(config[i].Consumer_key, config[i].Consumer_secret)
|
||||||
|
token := oauth1.NewToken(config[i].Access_token_key, config[i].Access_token_secret)
|
||||||
|
httpClient := configu.Client(oauth1.NoContext, token)
|
||||||
|
// twitter client
|
||||||
|
client := twitter.NewClient(httpClient)
|
||||||
|
clients = append(clients, client)
|
||||||
|
botnet.ScreenNames = append(botnet.ScreenNames, config[i].Title)
|
||||||
|
}
|
||||||
|
botnet.Clients = clients
|
||||||
|
|
||||||
|
fmt.Println("connection successfull")
|
||||||
|
|
||||||
|
return botnet
|
||||||
|
}
|
||||||
19
readKeywordsConfig.go
Normal file
19
readKeywordsConfig.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readKeywordsConfig() []string {
|
||||||
|
var keywords []string
|
||||||
|
file, e := ioutil.ReadFile("keywordsConfig.json")
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println("error:", e)
|
||||||
|
}
|
||||||
|
content := string(file)
|
||||||
|
json.Unmarshal([]byte(content), &keywords)
|
||||||
|
|
||||||
|
return keywords
|
||||||
|
}
|
||||||
19
readRepliesConfig.go
Normal file
19
readRepliesConfig.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func readRepliesConfig() []string {
|
||||||
|
var replies []string
|
||||||
|
file, e := ioutil.ReadFile("repliesConfig.json")
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println("error:", e)
|
||||||
|
}
|
||||||
|
content := string(file)
|
||||||
|
json.Unmarshal([]byte(content), &replies)
|
||||||
|
|
||||||
|
return replies
|
||||||
|
}
|
||||||
108
streamTweets.go
Normal file
108
streamTweets.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dghubble/go-twitter/twitter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isRT(tweet *twitter.Tweet) bool {
|
||||||
|
tweetWords := strings.Split(tweet.Text, " ")
|
||||||
|
for i := 0; i < len(tweetWords); i++ {
|
||||||
|
if tweetWords[i] == "RT" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func isFromOwnBot(flock Botnet, tweet *twitter.Tweet) bool {
|
||||||
|
for i := 0; i < len(flock.ScreenNames); i++ {
|
||||||
|
if flock.ScreenNames[i] == tweet.User.ScreenName {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRandomReplyFromReplies(replies []string) string {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
random := rand.Intn(len(replies))
|
||||||
|
return replies[random]
|
||||||
|
}
|
||||||
|
|
||||||
|
func replyTweet(client *twitter.Client, text string, inReplyToStatusID int64) {
|
||||||
|
tweet, httpResp, err := client.Statuses.Update(text, &twitter.StatusUpdateParams{
|
||||||
|
InReplyToStatusID: inReplyToStatusID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
if httpResp.Status != "200 OK" {
|
||||||
|
c.Red("error: " + httpResp.Status)
|
||||||
|
c.Purple("maybe twitter has blocked the account, CTRL+C, wait 15 minutes and try again")
|
||||||
|
}
|
||||||
|
fmt.Print("tweet posted: ")
|
||||||
|
c.Green(tweet.Text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func startStreaming(botnet Botnet, bot *twitter.Client, botScreenName string, keywords []string, replies []string) {
|
||||||
|
// Convenience Demux demultiplexed stream messages
|
||||||
|
demux := twitter.NewSwitchDemux()
|
||||||
|
demux.Tweet = func(tweet *twitter.Tweet) {
|
||||||
|
if isRT(tweet) == false && isFromOwnBot(botnet, tweet) == false {
|
||||||
|
//processTweet(botnetUser, botScreenName, keywords, tweet)
|
||||||
|
fmt.Println("[bot @" + botScreenName + "] - New tweet detected:")
|
||||||
|
c.Yellow(tweet.Text)
|
||||||
|
reply := getRandomReplyFromReplies(replies)
|
||||||
|
fmt.Print("reply: ")
|
||||||
|
c.Green(reply)
|
||||||
|
fmt.Println(tweet.User.ScreenName)
|
||||||
|
fmt.Println(tweet.ID)
|
||||||
|
replyTweet(bot, "@"+tweet.User.ScreenName+" "+reply, tweet.ID)
|
||||||
|
waitMinutes(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
demux.DM = func(dm *twitter.DirectMessage) {
|
||||||
|
fmt.Println(dm.SenderID)
|
||||||
|
}
|
||||||
|
demux.Event = func(event *twitter.Event) {
|
||||||
|
fmt.Printf("%#v\n", event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Starting Stream...")
|
||||||
|
// FILTER
|
||||||
|
filterParams := &twitter.StreamFilterParams{
|
||||||
|
Track: keywords,
|
||||||
|
StallWarnings: twitter.Bool(true),
|
||||||
|
}
|
||||||
|
stream, err := bot.Streams.Filter(filterParams)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
// Receive messages until stopped or stream quits
|
||||||
|
demux.HandleChan(stream.Messages)
|
||||||
|
|
||||||
|
// Wait for SIGINT and SIGTERM (HIT CTRL-C)
|
||||||
|
/*ch := make(chan os.Signal)
|
||||||
|
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
log.Println(<-ch)
|
||||||
|
|
||||||
|
fmt.Println("Stopping Stream...")
|
||||||
|
stream.Stop()*/
|
||||||
|
}
|
||||||
|
func streamTweets(botnet Botnet, keywords []string, replies []string) {
|
||||||
|
fmt.Println("total keywords: " + strconv.Itoa(len(keywords)))
|
||||||
|
c.Purple("keywords to follow: ")
|
||||||
|
fmt.Println(keywords)
|
||||||
|
c.Green("Starting to stream tweets")
|
||||||
|
for i := 0; i < len(botnet.Clients); i++ {
|
||||||
|
go startStreaming(botnet, botnet.Clients[i], botnet.ScreenNames[i], keywords, replies)
|
||||||
|
//wait 35 seconds to start the next bot
|
||||||
|
waitSeconds(35)
|
||||||
|
}
|
||||||
|
}
|
||||||
22
waitTime.go
Normal file
22
waitTime.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func waitMinutes(minutes int) {
|
||||||
|
//wait to avoid the twitter api limitation
|
||||||
|
timeToSleep := time.Duration(minutes) * time.Minute
|
||||||
|
fmt.Println("waiting " + strconv.Itoa(minutes) + " min to avoid twitter api limitation")
|
||||||
|
fmt.Println(time.Now().Local())
|
||||||
|
time.Sleep(timeToSleep)
|
||||||
|
}
|
||||||
|
func waitSeconds(seconds int) {
|
||||||
|
//wait to avoid the twitter api limitation
|
||||||
|
timeToSleep := time.Duration(seconds) * time.Second
|
||||||
|
fmt.Println("waiting " + strconv.Itoa(seconds) + " seconds to avoid twitter api limitation")
|
||||||
|
fmt.Println(time.Now().Local())
|
||||||
|
time.Sleep(timeToSleep)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user