forked from Lonami/Talaria
241 lines
6.5 KiB
Rust
241 lines
6.5 KiB
Rust
#![cfg(target_os = "android")]
|
|
#![allow(non_snake_case)]
|
|
|
|
use std::ffi::{CStr, CString};
|
|
use std::future::Future;
|
|
|
|
use grammers_client::{Client, Config};
|
|
use grammers_client::types::{Dialog, LoginToken};
|
|
use grammers_session::{PackedChat, Session};
|
|
use jni::JNIEnv;
|
|
use jni::objects::{JObject, JString};
|
|
use jni::sys::{jboolean, jint, jlong, jstring};
|
|
use log;
|
|
use log::{error, info, Level};
|
|
use once_cell::sync::OnceCell;
|
|
use tokio::runtime;
|
|
use tokio::runtime::Runtime;
|
|
|
|
const LOG_MIN_LEVEL: Level = Level::Trace;
|
|
const LOG_TAG: &str = ".native.talari";
|
|
const API_ID: i32 = 0;
|
|
const API_HASH: &str = "";
|
|
|
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
|
|
|
static RUNTIME: OnceCell<Runtime> = OnceCell::new();
|
|
static CLIENT: OnceCell<Client> = OnceCell::new();
|
|
|
|
fn block_on<F: Future>(future: F) -> F::Output {
|
|
if RUNTIME.get().is_none() {
|
|
RUNTIME
|
|
.set(
|
|
runtime::Builder::new_current_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap(),
|
|
)
|
|
.ok();
|
|
}
|
|
|
|
RUNTIME.get().unwrap().block_on(future)
|
|
}
|
|
|
|
async fn init_client() -> Result<()> {
|
|
android_logger::init_once(
|
|
android_logger::Config::default()
|
|
.with_min_level(LOG_MIN_LEVEL)
|
|
.with_tag(LOG_TAG),
|
|
);
|
|
|
|
if CLIENT.get().is_some() {
|
|
info!("Client is already initialized");
|
|
return Ok(());
|
|
}
|
|
|
|
info!("Connecting to Telegram...");
|
|
|
|
let client = Client::connect(Config {
|
|
session: Session::new(),
|
|
api_id: API_ID,
|
|
api_hash: API_HASH.to_string(),
|
|
params: Default::default(),
|
|
})
|
|
.await?;
|
|
|
|
info!("Connected!");
|
|
|
|
CLIENT
|
|
.set(client)
|
|
.map_err(|_| "Client was already initialized")?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn need_login() -> Result<bool> {
|
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
|
Ok(client.is_authorized().await?)
|
|
}
|
|
|
|
async fn request_login_code(phone: &str) -> Result<LoginToken> {
|
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
|
let token = client.request_login_code(&phone, API_ID, API_HASH).await?;
|
|
Ok(token)
|
|
}
|
|
|
|
async fn sign_in(token: LoginToken, code: &str) -> Result<()> {
|
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
|
client.sign_in(&token, &code).await?;
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_dialogs() -> Result<Vec<Dialog>> {
|
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
|
|
|
let mut result = Vec::new();
|
|
let mut dialogs = client.iter_dialogs();
|
|
while let Some(dialog) = dialogs.next().await? {
|
|
result.push(dialog);
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
async fn send_message(chat: PackedChat, text: &str) -> Result<()> {
|
|
let client = CLIENT.get().ok_or("Client not initialized")?;
|
|
client.send_message(chat, text).await?;
|
|
Ok(())
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_initClient(_: JNIEnv, _: JObject) {
|
|
match block_on(init_client()) {
|
|
Ok(_) => info!("Client initialized"),
|
|
Err(e) => error!("Failed to initialize client: {}", e),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_needLogin(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
) -> jboolean {
|
|
match block_on(need_login()) {
|
|
Ok(login) => login.into(),
|
|
Err(e) => {
|
|
error!("Failed to check if user is authorized: {}", e);
|
|
false.into()
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_requestLoginCode(
|
|
env: JNIEnv,
|
|
_: JObject,
|
|
phone: JString,
|
|
) -> jlong {
|
|
let phone = CString::from(CStr::from_ptr(env.get_string(phone).unwrap().as_ptr()));
|
|
|
|
match block_on(request_login_code(phone.to_str().unwrap())) {
|
|
Ok(token) => Box::into_raw(Box::new(token)) as jlong,
|
|
Err(e) => {
|
|
error!("Failed to request login code: {}", e);
|
|
0 as jlong
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_signIn(
|
|
env: JNIEnv,
|
|
_: JObject,
|
|
token_ptr: jlong,
|
|
code: JString,
|
|
) {
|
|
let token = *Box::from_raw(token_ptr as *mut LoginToken);
|
|
let code = CString::from(CStr::from_ptr(env.get_string(code).unwrap().as_ptr()));
|
|
|
|
match block_on(sign_in(token, code.to_str().unwrap())) {
|
|
Ok(_) => info!("Sign in success"),
|
|
Err(e) => error!("Failed to sign in: {}", e),
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_getDialogs(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
) -> jlong {
|
|
match block_on(get_dialogs()) {
|
|
Ok(dialogs) => Box::into_raw(Box::new(dialogs)) as jlong,
|
|
Err(e) => {
|
|
error!("Failed to get dialogs: {}", e);
|
|
0 as jlong
|
|
}
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogCount(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
dialogs_ptr: jlong,
|
|
) -> jint {
|
|
let dialogs = Box::leak(Box::from_raw(dialogs_ptr as *mut Vec<Dialog>));
|
|
dialogs.len() as jint
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogPacked(
|
|
env: JNIEnv,
|
|
_: JObject,
|
|
dialogs_ptr: jlong,
|
|
index: jint,
|
|
) -> jstring {
|
|
let dialogs = Box::leak(Box::from_raw(dialogs_ptr as *mut Vec<Dialog>));
|
|
|
|
let packed = dialogs[index as usize].chat().pack().to_hex();
|
|
let output = env.new_string(packed).unwrap();
|
|
output.into_inner()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_dialogTitle(
|
|
env: JNIEnv,
|
|
_: JObject,
|
|
dialogs_ptr: jlong,
|
|
index: jint,
|
|
) -> jstring {
|
|
let dialogs = Box::leak(Box::from_raw(dialogs_ptr as *mut Vec<Dialog>));
|
|
|
|
let title = dialogs[index as usize].chat().name();
|
|
let output = env.new_string(title).unwrap();
|
|
output.into_inner()
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_freeDialogs(
|
|
_: JNIEnv,
|
|
_: JObject,
|
|
dialogs_ptr: jlong,
|
|
) {
|
|
let _ = Box::from_raw(dialogs_ptr as *mut Vec<Dialog>);
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn Java_dev_lonami_talaria_bindings_Native_sendMessage(
|
|
env: JNIEnv,
|
|
_: JObject,
|
|
packed: JString,
|
|
text: JString,
|
|
) {
|
|
let packed = CString::from(CStr::from_ptr(env.get_string(packed).unwrap().as_ptr()));
|
|
let text = CString::from(CStr::from_ptr(env.get_string(text).unwrap().as_ptr()));
|
|
|
|
let packed = PackedChat::from_hex(packed.to_str().unwrap()).unwrap();
|
|
match block_on(send_message(packed, text.to_str().unwrap())) {
|
|
Ok(_) => info!("Message sent"),
|
|
Err(e) => error!("Failed to send message: {}", e),
|
|
}
|
|
}
|