hangups-rs

Rust version of hangups (WIP) Currently Login not possible
git clone git://archive.git.mtrnord.blog/Nordgedanken/hangups-rs.git
Log | Files | Refs | README | LICENSE

mod.rs (8022B)


      1 #[path = "proto/hangouts.rs"] mod hangouts;
      2 
      3 pub mod hangups {
      4     use hangups::hangouts::{MessageContent, Segment, SegmentType, RequestHeader,
      5                             EventRequestHeader, SendChatMessageRequest, DeliveryMediumType,
      6                             OffTheRecordStatus, EventType, SendChatMessageResponse,
      7                             DeliveryMedium, ConversationId};
      8     use protobuf::RepeatedField;
      9     use rand::random;
     10     use hyper::header::Cookie;
     11     use hyper::client::Request;
     12     use hyper::{Method, Uri};
     13     use hyper::error::Error;
     14     use protobuf::Message;
     15     use std::collections::HashMap;
     16     use std::time::*;
     17     use sha1::Sha1;
     18     use rustc_serialize::hex::ToHex;
     19     use rustc_serialize::base64::FromBase64;
     20     use protobuf::core::parse_from_bytes;
     21     use auth::get_auth;
     22     use helper::get_configs;
     23     use std;
     24 
     25     header! { (ContentType, "content-type") => [String] }
     26     header! {
     27               (XGoogEncodeResponseIfExecutable, "X-Goog-Encode-Response-If-Executable") => [String]
     28             }
     29 
     30     fn get_cookies() -> Result<HashMap<String, String>, Error> {
     31         let config = get_configs().unwrap();
     32         if (config.get_raw("SAPISID") == None) || (config.get_raw("HSID") == None) || (config.get_raw("SSID") == None) || (config.get_raw("APISID") == None) || (config.get_raw("SID") == None) {
     33             get_auth(String::from(""), String::from(""));
     34         }
     35 
     36         let sapisid_cookie = String::from(config.get_raw("SAPISID").unwrap().as_string().unwrap());
     37         let hsid_cookie = String::from(config.get_raw("HSID").unwrap().as_string().unwrap());
     38         let ssid_cookie = String::from(config.get_raw("SSID").unwrap().as_string().unwrap());
     39         let apisid_cookie = String::from(config.get_raw("APISID").unwrap().as_string().unwrap());
     40         let sid_cookie = String::from(config.get_raw("SID").unwrap().as_string().unwrap());
     41 
     42         let mut cookies = HashMap::new();
     43         cookies.insert(String::from("SAPISID"), sapisid_cookie);
     44         cookies.insert(String::from("HSID"), hsid_cookie);
     45         cookies.insert(String::from("SSID"), ssid_cookie);
     46         cookies.insert(String::from("APISID"), apisid_cookie);
     47         cookies.insert(String::from("SID"), sid_cookie);
     48         Ok(cookies)
     49     }
     50 
     51     fn get_auth_headers(sapisid: String) -> Result<HashMap<String, String>, Error> {
     52         let origin_url = String::from("https://talkgadget.google.com");
     53         let now = SystemTime::now();
     54         let dur = now.duration_since(UNIX_EPOCH).unwrap();
     55         let timestamp_msec = dur.as_secs();
     56 
     57 
     58         let auth_string = String::from(format!("{} {} {}", timestamp_msec, sapisid, origin_url));
     59         let mut hash = Sha1::new();
     60         hash.update(auth_string.as_bytes());
     61         let hash_bytes = hash.digest().bytes();
     62         let hex_sha1 = hash_bytes.to_hex();
     63         let sapisid_hash = format!("SAPISIDHASH {}_{}", timestamp_msec, hex_sha1);
     64         let mut auth_cookies = HashMap::new();
     65         auth_cookies.insert(String::from("authorization"), sapisid_hash);
     66         auth_cookies.insert(String::from("x-origin"), origin_url);
     67         auth_cookies.insert(String::from("x-goog-authuser"), String::from("0"));
     68         Ok(auth_cookies)
     69     }
     70 
     71     fn api_request(endpoint_url: String,
     72                    content_type: String,
     73                    response_type: String,
     74                    payload: std::vec::Vec<u8>)
     75                    -> Result<String, Error> {
     76        use hyper::Client;
     77        use tokio_core::reactor::Core;
     78        use futures::Future;
     79 
     80        let mut core = Core::new().unwrap();
     81        let handle = core.handle();
     82        let client = Client::configure()
     83                    .keep_alive(true)
     84                    .build(&handle);
     85         let url = format!("{}?key=AIzaSyAfFJCeph-euFSwtmqFZi0kaKk-cZ5wufM&alt={}",
     86                           endpoint_url,
     87                           response_type).parse::<Uri>().unwrap();;
     88         let auth_header = get_auth_headers(String::from("PLACEHOLDER"))
     89             .unwrap();
     90         let mut fresh_request = Request::new(Method::Post, url);
     91         let cookies = get_cookies().unwrap();
     92         let mut cookie_vec = Vec::new();
     93         for (key, val) in cookies {
     94             cookie_vec.push(format!("{}={}", key, val))
     95         }
     96         fresh_request.headers_mut().set(Cookie(cookie_vec));
     97         fresh_request.headers_mut().set(ContentType(String::from(content_type.to_owned())));
     98         fresh_request.headers_mut().set(XGoogEncodeResponseIfExecutable(String::from("base64".to_owned())));
     99         fresh_request.set_body(payload);
    100         for (key, val) in auth_header {
    101             fresh_request.headers_mut().append_raw(key, val.as_bytes().to_vec());
    102         }
    103         let body = String::new();
    104 
    105         let post = client.request(fresh_request)
    106                         .map(|res| {
    107                             let body = res.body();
    108                             println!("\n\nDone.");
    109                             body
    110                         });
    111 
    112         core.run(post).unwrap();
    113 
    114         Ok(body)
    115     }
    116 
    117     fn protobuf_api_request(api_endpoint: String,
    118                             request_struct: SendChatMessageRequest)
    119                             -> Result<SendChatMessageResponse, Error> {
    120         let url = format!("https://clients6.google.com/chat/v1/{}", api_endpoint);
    121         let payload = request_struct.write_to_bytes().unwrap();
    122         let output = api_request(url,
    123                                  String::from("application/x-protobuf"),
    124                                  String::from("proto"),
    125                                  payload);
    126         let decoded_output = output.unwrap().as_str().from_base64().unwrap();
    127         let response_proto = parse_from_bytes::<SendChatMessageResponse>(&decoded_output[..])
    128             .unwrap();
    129         Ok(response_proto)
    130     }
    131 
    132     pub fn send_message(message: String,
    133                         conv_id_raw: String)
    134                         -> Result<SendChatMessageResponse, Error> {
    135         //Prepare message
    136         let segment_type = SegmentType::SEGMENT_TYPE_TEXT;
    137         let mut segment_raw = Segment::new();
    138         segment_raw.set_field_type(segment_type);
    139         segment_raw.set_text(message);
    140         let mut segment = RepeatedField::new();
    141         segment.push(segment_raw);
    142         let mut message_content = MessageContent::new();
    143         message_content.set_segment(segment);
    144 
    145 
    146         //Prepare Request
    147         let mut request_header = RequestHeader::new();
    148         request_header.set_language_code(String::from("en"));
    149 
    150         let mut delivery_medium = DeliveryMedium::new();
    151         delivery_medium.set_medium_type(DeliveryMediumType::DELIVERY_MEDIUM_BABEL);
    152         let expected_otr = OffTheRecordStatus::OFF_THE_RECORD_STATUS_ON_THE_RECORD;
    153         let client_generated_id = random::<u64>();
    154         let event_type = EventType::EVENT_TYPE_REGULAR_CHAT_MESSAGE;
    155         let mut event_request_header = EventRequestHeader::new();
    156         let mut conv_id = ConversationId::new();
    157         conv_id.set_id(conv_id_raw);
    158         event_request_header.set_conversation_id(conv_id);
    159         event_request_header.set_delivery_medium(delivery_medium);
    160         event_request_header.set_expected_otr(expected_otr);
    161         event_request_header.set_client_generated_id(client_generated_id);
    162         event_request_header.set_event_type(event_type);
    163 
    164         let mut request = SendChatMessageRequest::new();
    165         request.set_request_header(request_header);
    166         request.set_message_content(message_content);
    167         request.set_event_request_header(event_request_header);
    168 
    169         let output = protobuf_api_request(String::from("conversations/sendchatmessage"), request)
    170             .unwrap();
    171         Ok(output)
    172     }
    173 }
    174 
    175 #[cfg(test)]
    176 mod tests {
    177     #[test]
    178     fn test_send_message() {
    179         use super::hangups::send_message;
    180 
    181         let test_message = String::from("Diese Nachricht sendet der Bot!");
    182         let test_conv = String::from("Ugxu9JRlbNPSqk5Ye1V4AaABAQ");
    183         send_message(test_message, test_conv);
    184     }
    185 }