├── .gitignore ├── Cargo.toml ├── README.md └── src ├── main.rs ├── msg.rs └── read.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | 13 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 14 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 15 | Cargo.lock 16 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "shlack" 3 | version = "0.1.0" 4 | authors = ["Vignesh Mohankumar "] 5 | 6 | [dependencies] 7 | argparse = "*" 8 | slack = "0.13.0" 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Shlack 2 | 3 | A command line utility to pipe text into Slack messages. 4 | 5 | ```sh 6 | $ echo "anyone wanna grab lunch? :thumbsup:" | shlack random 7 | ``` 8 | ```sh 9 | $ gist < file.json | shlack luke 10 | ``` 11 | ```sh 12 | $ hub pull-request | shlack general 13 | ``` 14 | 15 | ### Install: 16 | Set an environment variable `SLACK_TOKEN`. You can get a token here: https://api.slack.com/docs/oauth-test-tokens 17 | ```sh 18 | $ cargo build 19 | $ echo hi | target/debug/shlack 20 | ``` 21 | This sends a message to Slackbot. 22 | 23 | ### Commands: 24 | ``` 25 | -v --verbose 26 | -p --prepend 27 | -a --append 28 | ``` 29 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate argparse; 2 | extern crate slack; 3 | 4 | pub mod msg; 5 | pub mod read; 6 | 7 | use std::io::{self, BufRead}; 8 | use std::process::exit; 9 | 10 | use msg::Msg; 11 | use read::{Args, Token}; 12 | 13 | fn main() { 14 | let mut input = String::new(); 15 | let stdin = io::stdin(); 16 | let mut handle = stdin.lock(); 17 | if let Ok(_) = handle.read_line(&mut input) { 18 | if let Ok(_) = run_command(input) { 19 | return exit(0) 20 | } 21 | } 22 | exit(1) 23 | } 24 | 25 | fn run_command(input: String) -> Result<(), ()> { 26 | let args = Args::read(); 27 | let token = match Token::get() { 28 | Ok(Token(token)) => token, 29 | Err(e) => { 30 | if args.verbose { println!("{}", e) } 31 | return Err(()) 32 | }, 33 | }; 34 | let text = format!("{}{}{}", args.prepend.clone(), input, args.append.clone()); 35 | match Msg::new(text, args.channel.clone(), token).send() { 36 | Ok(_) => Ok(()), 37 | Err(e) => { 38 | if args.verbose { println!("{}", e) } 39 | Err(()) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/msg.rs: -------------------------------------------------------------------------------- 1 | use slack; 2 | 3 | pub struct Msg { 4 | text: String, 5 | channel: String, 6 | token: String, 7 | } 8 | 9 | impl Msg { 10 | pub fn new(text: String, channel: String, token: String) -> Msg { 11 | Msg { 12 | text: text, 13 | channel: channel, 14 | token: token, 15 | } 16 | } 17 | 18 | pub fn send(&self) -> Result<(), String> { 19 | let mut client = slack::RtmClient::new(&self.token); 20 | try!(client 21 | .login() 22 | .map_err(|_| "unable to login to slack".to_string())); 23 | let addr = try!(self.find_addr(client.get_channels()) 24 | .or(self.find_addr(client.get_users())) 25 | .or(self.find_addr(client.get_groups())) 26 | .ok_or("unable to find channel".to_string())); 27 | try!(client 28 | .post_message(&addr, &self.text.clone(), None) 29 | .map_err(|_| "unable to send message".to_string())); 30 | Ok(()) 31 | } 32 | 33 | fn find_addr(&self, recipients: Vec) -> Option { 34 | recipients 35 | .iter() 36 | .find(|x| x.name() == self.channel) 37 | .map(|x| x.id()) 38 | } 39 | } 40 | 41 | trait HasId { 42 | fn id(&self) -> String; 43 | } 44 | 45 | impl HasId for slack::Channel { 46 | fn id(&self) -> String { 47 | self.id.to_string() 48 | } 49 | } 50 | impl HasId for slack::User { 51 | fn id(&self) -> String { 52 | self.id.to_string() 53 | } 54 | } 55 | 56 | impl HasId for slack::Group { 57 | fn id(&self) -> String { 58 | self.id.to_string() 59 | } 60 | } 61 | 62 | trait HasName { 63 | fn name(&self) -> String; 64 | } 65 | 66 | impl HasName for slack::Channel { 67 | fn name(&self) -> String { 68 | self.name.to_string() 69 | } 70 | } 71 | 72 | impl HasName for slack::User { 73 | fn name(&self) -> String { 74 | self.name.to_string() 75 | } 76 | } 77 | 78 | impl HasName for slack::Group { 79 | fn name(&self) -> String { 80 | self.name.to_string() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/read.rs: -------------------------------------------------------------------------------- 1 | use argparse::{ArgumentParser, StoreTrue, Store}; 2 | 3 | use std::env; 4 | 5 | pub struct Args { 6 | pub verbose: bool, 7 | pub channel: String, 8 | pub prepend: String, 9 | pub append: String, 10 | } 11 | 12 | impl Args { 13 | pub fn read() -> Args { 14 | let mut verbose = false; 15 | let mut channel = "slackbot".to_string(); 16 | let mut prepend = "".to_string(); 17 | let mut append = "".to_string(); 18 | 19 | { 20 | let mut ap = ArgumentParser::new(); 21 | ap.set_description("Pipe text into Slack."); 22 | ap.refer(&mut channel) 23 | .add_argument("channel", Store, "Channel to send to"); 24 | ap.refer(&mut verbose) 25 | .add_option(&["-v", "--verbose"], StoreTrue, "Be verbose"); 26 | ap.refer(&mut prepend) 27 | .add_option(&["-p", "--prepend"], Store, "Text to prepend to input on message"); 28 | ap.refer(&mut append) 29 | .add_option(&["-a", "--append"], Store, "Text to append to input on message"); 30 | ap.parse_args_or_exit(); 31 | } 32 | 33 | Args { 34 | verbose: verbose, 35 | channel: channel, 36 | prepend: prepend, 37 | append: append, 38 | } 39 | } 40 | } 41 | 42 | pub struct Token(pub String); 43 | 44 | impl Token { 45 | pub fn get() -> Result { 46 | let token_var = "SLACK_TOKEN"; 47 | env::var(&token_var) 48 | .map(|t| Token(t)) 49 | .map_err(|_| format!("environment variable {} not found", token_var)) 50 | } 51 | } 52 | --------------------------------------------------------------------------------