Skip to content
Snippets Groups Projects
O

outvoke

Project ID: 53441769

Outvoke

This is a library, and at the same time a framework, and at the same time an internal DSL.

If you need Japanese Edition of this README, see README.ja.md

CAUTION

The author, cleemy desu wayo, has only tested it on Linux.

During the summer and fall of 2024, potentially disruptive changes will be made that will result in incompatibility. I will try to make the sample code I put under samples/ work as well as before.

The last version before these changes were made is version 0.0.99.20240619.

Requirements

  • Ruby 3.0 or later

What's This?

Outvoke makes it easy to write code that hooks into the data stream.

Setup

Put outvoke.rb in the current directory and give it execute permission.

Start it using the -e option as follows, and if you see output every 5 seconds, it is running for now.

$ ./outvoke.rb -e 'hook "every-sec", /[012][0-9]:[0-9][0-9]:[0-9][05]/'
# starting Outvoke 0.1 (version 0.0.99.20240815) ---- 2024-08-16 12:46:06 +0900
# ----
# given ruby code:
# hook "every-sec", /[012][0-9]:[0-9][0-9]:[0-9][05]/
# ----

[2024-08-16 12:46:06 +0900] [outvoke-system] vrchat-001: a new log file was found.
[2024-08-16 12:46:07 +0900] [outvoke-system] vrchat-001: first time log check has done.
[2024-08-16 12:46:10 +0900] 2024-08-16 12:46:10 +0900
[2024-08-16 12:46:15 +0900] 2024-08-16 12:46:15 +0900
[2024-08-16 12:46:20 +0900] 2024-08-16 12:46:20 +0900
[2024-08-16 12:46:25 +0900] 2024-08-16 12:46:25 +0900
[2024-08-16 12:46:30 +0900] 2024-08-16 12:46:30 +0900

To exit, press Ctrl + C.

There may be error lines if VRChat is not installed or the log files are in a different location.

If you have VRChat installed and it has been a long time since VRChat was started, it may take a long time after Outvoke is started before you see the first time log check has been done. This time can be shortened by restarting VRChat.

Config File

If there is a file outvoke.conf.rb in the current directory, Outvoke will first include that file.

For example, if you have a line like the following in outvoke.conf.rb, you can specify the directory for VRChat log files to look for.

$outvoke.sources['vrchat-001'].log_dir = "#{Dir.home}/.steam/debian-installation/steamapps/compatdata/438100/pfx/drive_c/users/steamuser/AppData/LocalLow/VRChat/VRChat"

Basic Usage

When you run Outvoke with no arguments, it looks for main.rb in the current directory and includes it.

You can specify a file name or write a one-liner using the -e option, but for the sake of clarity, this README.md assumes that you are writing in main.rb.

If you write "using OutvokeDSL" at the beginning of main.rb, you will be in the mode of internal DSL. You can write it as casually as writing a configuration file.

Since main.rb is interpreted as a Ruby script, you can write complex programs.

The following is an example of main.rb.

using OutvokeDSL

hook 'every-sec', /05:00:00/ do
  spawn 'play', '-q', '-t', 'wav', '-v', '0.6', 'ring.wav', 'repeat', '50'
end

At 5:00 AM, ring.wav in the current directory is played by the play command.

(The rest is currently being written)

With VRChat

Some explanations and samples may be found at the following:

Also see the samples below.

a video of samples/2024/vrchat_multiple_ds2.rb in action: https://x.com/metanagi/status/1802512101111689368

(The rest is currently being written)

One-liner examples

Even with one-liners, basically all of Ruby's features are available.

Before you run them, you'll need the following:

  1. Put outvoke.rb to the current directory

  2. Set up maoudamashii-se-system47.wav to the current directory

(download a wav file from https://maou.audio/se_system47/ )

  1. Check to see if sound is played by the play command
$ play maoudamashii-se-system47.wav
  1. Put outvoke.conf.rb to the current directory with the following contents
def ring(vol = "0.2")
  spawn "play", "-v", vol.to_s, "-q", "maoudamashii-se-system47.wav", :err=>"/dev/null"
end
  1. Check to see if the sound is played once per second with the following one-liner
$ ./outvoke.rb -e 'hook "every-sec", /./ do ring ; end'

In version 0.0.99.20240818 or later, the following is also possible:

$ ./outvoke.rb -e 'hook "every-sec" do ring ; end'

You can also write as follows:

$ ./outvoke.rb -e 'hook("every-sec"){ring}'

Notes on the one-liners presented here

These one-liners make heavy use of _1 (numbered parameter).

$ ./outvoke.rb -e 'hookvr(/pickup object/i){puts _1.body}'

The above is the same as:

$ ./outvoke.rb -e 'hookvr(/pickup object/i){|e| puts e.body}'

As of August 2024, hook "vrchat-001" is the same as hookvr, but these one-liners dare not use hookvr.

Slightly complicated alarm clock one-lilners

Now, here are a number of one-liner examples.

Sound three times every 10 seconds between 5:00 AM and 05:10 AM:

$ ./outvoke.rb -e 'hook("every-sec", / 05:0[0-9]:[0-5][036]/){ring}'

Play the video once every 5 minutes between 05:00 AM and 05:55 AM:

$ ./outvoke.rb -e 'hook("every-sec", / 05:[0-5][05]:00/) {spawn "mpv", "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "--really-quiet", :err=>"/dev/null"}'

Sound at exactly 05:00 AM, but only between Monday and Friday:

$ ./outvoke.rb -e 'hook("every-sec", / 05:00:00/){ring if (1..5).include?(Time.now.wday)}'

Instead of Time.now, you can also use _1.status.now:

$ ./outvoke.rb -e 'hook("every-sec", / 05:00:00/){ring if (1..5).include?(_1.status.now.wday)}'

simple VRChat-related one-liners

Play a sound when someone joins:

$ ./outvoke.rb -e 'hook("vrchat-001", /onplayerjoincomplete/i){ring}'

/onplayerjoined/i is not recommended.

Play a sound when you pick up an object:

$ ./outvoke.rb -e 'hook("vrchat-001", /pickup object/i){ring}'

Display all video-related logs at all:

$ ./outvoke.rb -e 'hook "vrchat-001", /(resolv|video)/i'

If you write "resolv", it will match both "resolve" and "resolving".

Display all TopazChat-related logs at all:

$ ./outvoke.rb -e 'hook "vrchat-001", /(rtsp|topaz)/i'

Slightly complicated VRChat-related one-liners

Play a sound when someone joins, but no ringing for 90 seconds after you join:

$ ./outvoke.rb -e 'hook("vrchat-001", /onplayerjoincomplete/i) {ring if _1.status.elapsed > 90}'

You can get the elapsed time since you joined by _1.status.elapsed.

This will prevent unnecessary noise immediately after you join an instance with a large number of people.

When you want to output string even without sound:

$ ./outvoke.rb -e 'hook("vrchat-001", /onplayerjoincomplete/i) {ring if _1.status.elapsed > 90 ; _1.body}'

Whenever a video starts playing in the world, play that video in mpv:

$ ./outvoke.rb -e 'hook("vrchat-001", /video playback.*resolve url..(https[-_.a-zA-Z0-9&=?%:\/]*)/i) {spawn "mpv", _1.m[1], "--really-quiet", :err=>"/dev/null"; _1.m[1]}'

If an error occurs in a video player in the world, mpv may start multiple times due to retries.

(More and more samples to be added)