Skip to content

Commit

Permalink
Improvements
Browse files Browse the repository at this point in the history
* If one channel fails, don't exit
* Remove hardcoded channels and let user define channels and specify
them with --channel/-ch
* Support for specifying (multiple) channels inline
* Specify multiple channels defined in config.ini
* Update README.md
  • Loading branch information
dubsec committed Jan 26, 2021
1 parent b0c09c1 commit 87a8b44
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 80 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
bin/
dist/
*.tmp
.vscode/
*.exe
36 changes: 28 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ Options:
Examples:
emissary -telegram --message "Hello telegram"
cat domins.txt | emissary --slack --stdin
cat domains.txt | emissary --slack --stdin
emissary --channel Discord -m "It works!!!"
emissary -ch Discord -ch Telegram -m "Your message"
emissary -in "webhook:=https://api.telegram.org/botxxxxx/sendMessage§data:={'chat_id': 'xxxx'}" -in "webhook:=https://hooks.slack.com/services/xxxxx" -m "Hack the planet!"
```

**Create ~/.config/emissary.ini with the following:**
### Create ~/.config/emissary.ini with the following:
```
[Telegram]
webhook=https://api.telegram.org/botxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxx/sendMessage
Expand All @@ -64,7 +66,7 @@ subject="New domains found!"
Now you can start using emissary :)


**Custom Webhooks**
### Custom Webhooks

It's possible to add your own channels as well, adding Discord as a custom channel looks like this:

Expand All @@ -85,33 +87,51 @@ The following fields can be used for a given channel:
| data | If you want to send additional data, you can specify that here as a json formatted string, e.g. `data={"someKey": "someValue", "otherKey": "otherValue"}`. |


**Pipe data via stdin:**
### Pipe data via stdin:
```
$ cat domains.txt | emissary --telegram --stdin
```

**Specify a message as an argument:**
### Specify a message as an argument:
```
$ emissary --telegram --message "This is a very cool message"
```

**Send to multiple channels:**
### Send to multiple channels:
```
$ cat domains.txt | emissary -t -s -si
```

**Send only 10 lines:**
### Send only 10 lines:
```
$ cat domains.txt | emissary -t -si --rows 10
```

**Send everything from the file:**
### Send everything from the file:
```
$ cat domains.txt | emissary -t -si -r 0
```

Emissary will only send 20 rows by default, this is to protect against accidentally sending a gazillion domains :) It can be overwritten with `--rows 0` which means unlimited rows.

### Multiple inline webhooks

It's possible use multiple webhooks directly on the command line without specifying them in `config.ini`.

The following command will send `Hack the planet` to Telegram and Slack:

```
emissary -in "webhook:=https://api.telegram.org/botxxxxx/sendMessage§data:={'chat_id': 'xxxx'}" -in "webhook:=https://hooks.slack.com/services/xxxxx" -m "Hack the planet!"
```

The same fields in `config.ini` are used inline as well. They can be used like so:

- `webhook:=<url>`
- `textField:=<key>`
- `data:=<additional json>`

The character `§` is used as a delimiter between each field.

## Contributing
Any feedback or ideas are welcome! Want to improve something? Create a pull request!

Expand Down
94 changes: 67 additions & 27 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,81 @@ import (
"flag"
"fmt"
"os"
"strings"
)

type channels []string

func (c *channels) String() string {
return ""
}

func (c *channels) Set(value string) error {
*c = append(*c, value)
return nil
}

type inlines struct {
hooks []inline
}

type inline struct {
webhook string
textField string
data string
}

func (i *inlines) String() string {
return "{'lol':1}"
}

func (i *inlines) Set(value string) error {
split := strings.Split(value, "§")
mul := inlines{}
final := inline{}
for _, val := range split {
s := strings.Split(val, ":=")

if strings.ToLower(s[0]) == "webhook" {
final.webhook = s[1]
}

if strings.ToLower(s[0]) == "textField" {
final.textField = s[1]
}

if strings.ToLower(s[0]) == "data" {
final.data = s[1]
}

}
mul.hooks = append(mul.hooks, final)
*i = mul

return nil
}

type cliOptions struct {
telegram bool
discord bool
slack bool
email bool
teams bool
version bool
stdin bool
message string
channel string
inline string
data string
text string
rows int
email bool
version bool
stdin bool
message string
channel channels
inline inlines
data string
text string
rows int
}

func processArgs() cliOptions {

opts := cliOptions{}
flag.BoolVar(&opts.telegram, "telegram", false, "Send via telegram")
flag.BoolVar(&opts.telegram, "t", false, "Send via telegram")
flag.BoolVar(&opts.slack, "slack", false, "Send via slack")
flag.BoolVar(&opts.slack, "s", false, "Send via slack")
flag.BoolVar(&opts.email, "email", false, "Send via smtp")
flag.BoolVar(&opts.email, "e", false, "Send via smtp")
flag.BoolVar(&opts.teams, "teams", false, "Send via Microsoft Teams")
flag.BoolVar(&opts.teams, "ms", false, "Send via Send via Microsoft Teams")
flag.StringVar(&opts.channel, "channel", "", "Specify a custom channel you have defined in ~/.config/emissary.ini")
flag.StringVar(&opts.channel, "ch", "", "Specify a custom channel you have defined in ~/.config/emissary.ini")
flag.StringVar(&opts.inline, "inline", "", "Specify channel directly in the command line")
flag.StringVar(&opts.inline, "in", "", "Specify channel directly in the command line")
flag.StringVar(&opts.text, "text", "text", "Specify the field that contains the message. Default is 'text'")
flag.StringVar(&opts.text, "txt", "text", "Specify the field that contains the message. Default is 'text'")
flag.Var(&opts.channel, "channel", "Specify a custom channel you have defined in ~/.config/emissary.ini")
flag.Var(&opts.channel, "ch", "Specify a custom channel you have defined in ~/.config/emissary.ini")
flag.Var(&opts.inline, "inline", "Specify channel directly in the command line")
flag.Var(&opts.inline, "in", "Specify channel directly in the command line")
flag.StringVar(&opts.text, "text", "", "Specify the field that contains the message. Default is 'text'")
flag.StringVar(&opts.text, "txt", "", "Specify the field that contains the message. Default is 'text'")
flag.StringVar(&opts.data, "data", "", "Specify json data that should be sent")
flag.StringVar(&opts.data, "d", "", "Specify json data that should be sent")
flag.BoolVar(&opts.version, "version", false, "Show version number")
Expand Down
3 changes: 1 addition & 2 deletions channels.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func WebhookRequest(webhook string, message string, msgField string, additionalD
jayson := map[string]interface{}{
msgField: message,
}

additionalData = strings.ReplaceAll(additionalData, "'", "\"")
if additionalData != "" {
data := []byte(`` + additionalData + ``)
var f interface{}
Expand All @@ -37,7 +37,6 @@ func WebhookRequest(webhook string, message string, msgField string, additionalD
jayson[k] = v
}
}

js, _ := json.Marshal(jayson)
return request(webhook, string(js))
}
Expand Down
79 changes: 36 additions & 43 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@ func checkResponse(httpResponse *http.Response, err error) {
}
log.Println("Response HTTP Status code: ", httpResponse.StatusCode)
log.Println("Response HTTP Body: ", string(body))
os.Exit(1)
}
}

if err != nil {
log.Println("Something went wrong sending your message: ", err)
os.Exit(1)
log.Printf("Something went wrong sending your message: %s\n", err)
}
}

Expand All @@ -57,7 +55,6 @@ func main() {
cfg, err := ini.Load(configPath)
if err != nil {
log.Fatal("Fail to read configuration file: ", err)
os.Exit(1)
}

if len(opts.message) > 0 && opts.stdin {
Expand All @@ -70,19 +67,21 @@ func main() {
os.Exit(1)
}

if opts.channel != "" {
webhook := cfg.Section(opts.channel).Key("webhook").String()
textField := cfg.Section(opts.channel).Key("textField").MustString("text")
additionalData := cfg.Section(opts.channel).Key("data").String()
if len(opts.inline.hooks) > 0 {

if webhook == "" {
fmt.Printf("[-] Channel %s does not contain a webhook...", opts.channel)
os.Exit(1)
}
for _, val := range opts.inline.hooks {
if val.webhook == "" {
log.Fatal("[-] Inline webhook does not contain webhook...")
}

resp, err := WebhookRequest(webhook, opts.message, textField, additionalData)
if val.textField == "" {
val.textField = "text"
}

checkResponse(resp, err)
resp, err := WebhookRequest(val.webhook, opts.message, val.textField, val.data)

checkResponse(resp, err)
}

}

Expand All @@ -93,12 +92,7 @@ func main() {
sc := bufio.NewScanner(os.Stdin)
msg := ""
for sc.Scan() {
// Stupid ms teams not handling new lines properly
if opts.teams {
msg = sc.Text() + "\n"
} else {
msg = sc.Text()
}
msg = sc.Text()
if opts.rows == 0 {
messages = append(messages, msg)
} else {
Expand All @@ -112,38 +106,37 @@ func main() {
}

messages = append(messages, fmt.Sprintf("--------\nSent %d lines", count))
fmt.Println(messages)

opts.message = strings.Join(messages[:], "\n")
}

if opts.telegram {
webhook := cfg.Section("Telegram").Key("webhook").String()
textField := cfg.Section("Telegram").Key("textField").MustString("text")
additionalData := cfg.Section("Telegram").Key("data").String()
if len(opts.channel) != 0 {
for _, ch := range opts.channel {
webhook := cfg.Section(ch).Key("webhook").String()
textField := cfg.Section(ch).Key("textField").MustString("text")
additionalData := cfg.Section(ch).Key("data").String()

resp, err := WebhookRequest(webhook, opts.message, textField, additionalData)

checkResponse(resp, err)
}

if opts.slack {
webhook := cfg.Section("Slack").Key("webhook").String()
textField := cfg.Section("Slack").Key("textField").MustString("text")
additionalData := cfg.Section("Slack").Key("data").String()

resp, err := WebhookRequest(webhook, opts.message, textField, additionalData)
if webhook == "" {
fmt.Printf("[-] Channel %s does not contain a webhook...", ch)
os.Exit(1)
}

checkResponse(resp, err)
}
// MS Teams hack for properly showing rows
if strings.HasPrefix(webhook, "https://outlook.office.com") {
split := strings.Split(opts.message, "\n")
newMessage := ""
for _, v := range split {
newMessage += v + "\n\n"
}
opts.message = newMessage
}

if opts.teams {
webhook := cfg.Section("Teams").Key("webhook").String()
textField := cfg.Section("Teams").Key("textField").MustString("text")
additionalData := cfg.Section("Teams").Key("data").String()
resp, err := WebhookRequest(webhook, opts.message, textField, additionalData)

resp, err := WebhookRequest(webhook, opts.message, textField, additionalData)
checkResponse(resp, err)

checkResponse(resp, err)
}
}

if opts.email {
Expand Down

0 comments on commit 87a8b44

Please sign in to comment.