teamtalk 6.0.0

TeamTalk SDK for Rust
Documentation
#[cfg(all(feature = "scripts", feature = "mock"))]
mod scripts_tests {
    use std::fs;
    use std::path::PathBuf;
    use std::time::{Duration, SystemTime, UNIX_EPOCH};

    use teamtalk::events::Event;
    use teamtalk::extensions::scripts::ScriptManager;
    use teamtalk::mock::MockMessage;
    use teamtalk::utils::ToTT;

    fn copy_tt(src: &str, dst: &mut [teamtalk::client::ffi::TTCHAR]) {
        let tt = src.tt();
        let len = tt.len().min(dst.len());
        dst[..len].copy_from_slice(&tt[..len]);
    }

    fn temp_script(name: &str, contents: &str) -> PathBuf {
        let mut path = std::env::temp_dir();
        let nanos = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap_or_else(|_| Duration::from_secs(0))
            .as_nanos();
        path.push(format!("teamtalk_{name}_{nanos}.lua"));
        fs::write(&path, contents).expect("write script");
        path
    }

    #[test]
    fn register_command_and_unload_removes_command() {
        let path = temp_script(
            "register_command",
            r#"
            register_command("ping", function(args)
                return true
            end)
        "#,
        );
        let mut manager = ScriptManager::new();
        manager.load_script("test", &path).expect("load");
        let handled = manager.call_command("ping", &[]).expect("call_command");
        assert!(handled);
        manager.unload_script("test").expect("unload");
        let handled = manager
            .call_command("ping", &[])
            .expect("call_command after unload");
        assert!(!handled);
        let _ = fs::remove_file(path);
    }

    #[test]
    fn timeout_interrupts_long_running_command() {
        let path = temp_script(
            "timeout",
            r#"
            register_command("spin", function(args)
                while true do end
            end)
        "#,
        );
        let mut manager = ScriptManager::new();
        manager.set_timeout(Duration::from_millis(1));
        manager.set_hook_instruction_count(1);
        manager.load_script("timeout", &path).expect("load");
        let err = manager
            .call_command("spin", &[])
            .expect_err("expected timeout");
        let msg = err.to_string();
        assert!(msg.contains("timeout"), "{msg}");
        manager.unload_script("timeout").expect("unload");
        let _ = fs::remove_file(path);
    }

    #[test]
    fn errors_include_context() {
        let path = temp_script(
            "event_error",
            r#"
            function on_event(evt)
                error("boom")
            end
        "#,
        );
        let mut manager = ScriptManager::new();
        manager.load_script("event_error", &path).expect("load");
        let message = MockMessage::empty();
        let err = manager
            .handle_event(Event::None, &message)
            .expect_err("expected error");
        let text = err.to_string();
        assert!(text.contains("lua on_event error (None)"), "{text}");
        manager.unload_script("event_error").expect("unload");
        let _ = fs::remove_file(path);
    }

    #[test]
    fn event_tables_include_new_payloads() {
        let path = temp_script(
            "event_payloads",
            r#"
            function on_event(evt)
                if evt.type == "FileNew" then
                    assert(evt.remote_file ~= nil)
                    assert(evt.remote_file.name == "clip.wav")
                elseif evt.type == "BannedUser" then
                    assert(evt.banned_user ~= nil)
                    assert(evt.banned_user.owner == "owner")
                elseif evt.type == "DesktopInput" then
                    assert(evt.desktop_input ~= nil)
                    assert(evt.desktop_input.key_code == 65)
                elseif evt.type == "StreamMediaFile" then
                    assert(evt.media_file_info ~= nil)
                    assert(evt.media_file_info.name == "stream.ogg")
                elseif evt.type == "AudioInput" then
                    assert(evt.audio_input_progress ~= nil)
                    assert(evt.audio_input_progress.stream_id == 9)
                end
                return false
            end
        "#,
        );
        let mut manager = ScriptManager::new();
        manager.load_script("payloads", &path).expect("load");

        let mut remote_file = teamtalk::client::ffi::RemoteFile {
            nChannelID: 1,
            nFileID: 2,
            ..Default::default()
        };
        copy_tt("clip.wav", &mut remote_file.szFileName);

        let mut banned_user = teamtalk::client::ffi::BannedUser::default();
        copy_tt("owner", &mut banned_user.szOwner);

        let desktop_input = teamtalk::client::ffi::DesktopInput {
            uMousePosX: 1,
            uMousePosY: 2,
            uKeyCode: 65,
            uKeyState: teamtalk::client::ffi::DesktopKeyState::DESKTOPKEYSTATE_DOWN as u32,
        };

        let mut media_file = teamtalk::client::ffi::MediaFileInfo::default();
        copy_tt("stream.ogg", &mut media_file.szFileName);

        let audio_input = teamtalk::client::ffi::AudioInputProgress {
            nStreamID: 9,
            uQueueMSec: 1,
            uElapsedMSec: 2,
        };

        manager
            .handle_event(
                Event::FileNew,
                &MockMessage::remote_file(Event::FileNew, remote_file),
            )
            .expect("file new");
        manager
            .handle_event(Event::BannedUser, &MockMessage::banned_user(banned_user))
            .expect("banned user");
        manager
            .handle_event(
                Event::DesktopInput,
                &MockMessage::desktop_input(desktop_input, 3),
            )
            .expect("desktop input");
        manager
            .handle_event(
                Event::StreamMediaFile,
                &MockMessage::media_file_info(Event::StreamMediaFile, media_file),
            )
            .expect("media file info");
        manager
            .handle_event(
                Event::AudioInput,
                &MockMessage::audio_input_progress(audio_input, 4),
            )
            .expect("audio input");

        manager.unload_script("payloads").expect("unload");
        let _ = fs::remove_file(path);
    }
}