reactphp / filesystem

Evented filesystem access.
MIT License
135 stars 40 forks source link

fget truncate alternative? #104

Open valzargaming opened 2 years ago

valzargaming commented 2 years ago

I'm hoping to port some of my existing code over to ReactPHP Filesystem to make reading lines from a file and truncating it afterwards asynchronous so that my main loop can still be working on other time sensitive tasks, like listening for events fired over a socket. The current documentation doesn't really give me a good idea of how to do this, and only some of the snippets seem to be relevant to my usage. What would be filesystem's equivalent to this?

if ($file = fopen($file_path, "r+")) {
    while (($fp = fgets($file, 4096)) !== false) {
        $fp = str_replace(PHP_EOL, "", $fp);
        //Do stuff with the line
    }
    ftruncate($file, 0);
    fclose($file);
}
WyriHaximus commented 2 years ago

For version 0.2 you could just do a $file->putContents(''); after a $file->getContents().

valzargaming commented 2 years ago

I've been working on getting some test code up and running and this is the farthest I've been able to get so far without any errors. The code runs fine until it gets past $filesystem->detect() in which case there are no errors but no messages ever get relayed to $target_channel. I'm unsure if there's an error within the scope and PHP isn't emitting an error, or if there was a race condition previously where the file is being wiped faster than the contents could be emitted because $file was being updated in the next step before the each loop resolved, or if there's something else entirely that I'm missing.

function chat_relay($filesystem, $guild, string $file_path, string $channel_id)
{    
    if ($target_channel = $guild->channels->offsetGet($channel_id)) {
        $filesystem->detect($file_path)->done(function (\React\Filesystem\Node\FileInterface $file) {
            $file->getContents()->then(function (string $contents) use ($file, $target_channel) {
                $contents = explode('\n', $contents);
                foreach ($contents as $line) {
                    $target_channel->sendMessage($line);
                }
            })->done(function () use ($file) {
                $file->putContents('');
            });
        });
    }
}
WyriHaximus commented 2 years ago

Try this:

function chat_relay($filesystem, $guild, string $file_path, string $channel_id)
{    
    if ($target_channel = $guild->channels->offsetGet($channel_id)) {
        $filesystem->detect($file_path)->then(function (\React\Filesystem\Node\FileInterface $file) {
            return $file->getContents()->then(function (string $contents) use ($file, $target_channel) {
                $contents = explode('\n', $contents);
                foreach ($contents as $line) {
                    $target_channel->sendMessage($line);
                }
            })->then(function () use ($file) {
                $file->putContents('');
            });
        })->done();
    }
}

If you're 100% sure $file_path is a file you can also use:

function chat_relay($filesystem, $guild, string $file_path, string $channel_id)
{    
    if ($target_channel = $guild->channels->offsetGet($channel_id)) {
            $file = $filesystem->file($file_path);
            $file->getContents()->then(function (string $contents) use ($file, $target_channel) {
                $contents = explode('\n', $contents);
                foreach ($contents as $line) {
                    $target_channel->sendMessage($line);
                }
            })->then(function () use ($file) {
                $file->putContents('');
            })->done();
    }
}
valzargaming commented 2 years ago

I've been having a lot of trouble with this function since trying to implement it. Our host reports that he's able to start up his bot with this function in it, but that it is either never called or is failing silently. The original code works fine with the hard-coded file path. He has reverted back to the original in the mean time until we can get more reliable logging.

WyriHaximus commented 2 years ago

but that it is either never called or is failing silently

That is rather crucial information to know. Other bits of information are hostos, PHP version, with event loop extension you are using.

valzargaming commented 2 years ago

We got logging and figured out the issue, it was on another line due to a typo that was made at the time of implementation.

valzargaming commented 2 years ago

It turns out that when I thought it was working our host had thrown up some old code that was prior to implementing filesystem, and it is indeed not working. I have some logs this time and additional code showing that no errors are thrown and yet no file contents ever appear to get passed through, nor does the file ever get truncated. I have also checked the exact file paths using nano /home/1713/civ13-rp/ooc.log and see that there is valid content. (also please note that the commented code at the top works fine)

Logs:

[2022-06-08T03:14:27.483759+02:00] New logger.DEBUG: Initializing DiscordPHP v7.1.0 (DiscordPHP-Http: v9.0.10 & Gateway: v9) on PHP 8.1.5 [] []
[2022-06-08T03:14:27.499499+02:00] New logger.DEBUG: BUCKET getoauth2/applications/@me queued REQ GET oauth2/applications/@me [] []
[2022-06-08T03:14:27.504857+02:00] New logger.DEBUG: BUCKET getgateway/bot queued REQ GET gateway/bot [] []
[2022-06-08T03:14:27.678281+02:00] New logger.DEBUG: REQ GET oauth2/applications/@me successful [] []
[2022-06-08T03:14:27.678348+02:00] New logger.DEBUG: http not checking {"waiting":1,"empty":true} []
[2022-06-08T03:14:27.743381+02:00] New logger.DEBUG: REQ GET gateway/bot successful [] []
[2022-06-08T03:14:27.743440+02:00] New logger.DEBUG: http not checking {"waiting":0,"empty":true} []
[2022-06-08T03:14:27.743498+02:00] New logger.INFO: gateway retrieved and set {"gateway":"wss://gateway.discord.gg/?v=9&encoding=json","session":{"total":1000,"remaining":969,"reset_after":70761159,"max_concurrency":1}} []
[2022-06-08T03:14:27.743522+02:00] New logger.INFO: starting connection to websocket {"gateway":"wss://gateway.discord.gg/?v=9&encoding=json"} []
[2022-06-08T03:14:27.880781+02:00] New logger.INFO: websocket connection has been created [] []
[2022-06-08T03:14:27.881243+02:00] New logger.INFO: received hello [] []
[2022-06-08T03:14:27.881284+02:00] New logger.DEBUG: sending heartbeat {"seq":null} []
[2022-06-08T03:14:27.881339+02:00] New logger.INFO: heartbeat timer initilized {"interval":41250.0} []
[2022-06-08T03:14:27.881374+02:00] New logger.INFO: identifying {"payload":{"op":2,"d":{"token":"xxxxxx","properties":{"$os":"Linux","$browser":"DiscordBot (https://github.com/discord-php/DiscordPHP-HTTP, v9.0.10)","$device":"DiscordBot (https://github.com/discord-php/DiscordPHP-HTTP, v9.0.10)","$referrer":"https://github.com/discord-php/DiscordPHP","$referring_domain":"https://github.com/discord-php/DiscordPHP"},"compress":true,"intents":98047}}} []
[2022-06-08T03:14:27.976638+02:00] New logger.DEBUG: received heartbeat ack {"response_time":95.27087211608887} []
[2022-06-08T03:14:28.083848+02:00] New logger.DEBUG: ready packet received [] []
[2022-06-08T03:14:28.083904+02:00] New logger.DEBUG: discord trace received {"trace":["[\"gateway-prd-main-9ctn\",{\"micros\":107122,\"calls\":[\"discord-sessions-blue-prd-2-102\",{\"micros\":103473,\"calls\":[\"start_session\",{\"micros\":83198,\"calls\":[\"discord-api-59f5fc6648-h4wnx\",{\"micros\":76944,\"calls\":[\"get_user\",{\"micros\":21176},\"add_authorized_ip\",{\"micros\":5094},\"get_guilds\",{\"micros\":7517},\"coros_wait\",{\"micros\":1}]}]},\"guilds_connect\",{\"micros\":2,\"calls\":[]},\"presence_connect\",{\"micros\":14609,\"calls\":[]}]}]}]"]} []
[2022-06-08T03:14:28.084478+02:00] New logger.DEBUG: client created and session id stored {"session_id":"df745b9ce3575a9d38b2e6d81e135103","user":{"id":"471400168973926430","username":"Piratebot","discriminator":"8117","avatar":"https://cdn.discordapp.com/avatars/471400168973926430/57ce931a30d8598a7cf40f5e2c8ba0f4.webp?size=1024","bot":true,"system":null,"mfa_enabled":true,"locale":null,"verified":true,"email":null,"flags":0,"banner":null,"accent_color":null,"premium_type":null,"public_flags":null}} []
[2022-06-08T03:14:28.085998+02:00] New logger.INFO: stored guilds {"count":0,"unavailable":1} []
[2022-06-08T03:14:28.149327+02:00] New logger.DEBUG: guild available {"guild":"468979034571931648","unavailable":1} []
[2022-06-08T03:14:28.149403+02:00] New logger.INFO: all guilds are now available {"count":1} []
[2022-06-08T03:14:28.149444+02:00] New logger.INFO: set up chunking, checking for chunks every 5 seconds [] []
[2022-06-08T03:14:28.149466+02:00] New logger.DEBUG: sending 1 chunks with 1 large guilds overall [] []
[2022-06-08T03:14:28.149491+02:00] New logger.DEBUG: sending chunk with 1 large guilds [] []
[2022-06-08T03:14:28.365666+02:00] New logger.DEBUG: received guild member chunk {"guild_id":"468979034571931648","guild_name":"Civ 13","chunk_count":654,"member_collection":1,"member_count":654} []
[2022-06-08T03:14:28.414496+02:00] New logger.DEBUG: parsed 653 members (skipped 1) {"repository_count":654,"actual_count":654} []
[2022-06-08T03:14:28.414578+02:00] New logger.DEBUG: all users have been loaded {"guild":"468979034571931648","member_collection":654,"member_count":654} []
[2022-06-08T03:14:28.414606+02:00] New logger.INFO: client is ready [] []
Logged in as Piratebot#8117 (471400168973926430)
------
[READY] Relay timer started!
[RELAY - PATH] /home/1713/civ13-rp/ooc.log
[RELAY - PATH] /home/1713/civ13-rp/admin.log
[RELAY - PATH] /home/1713/civ13-tdm/ooc.log
[RELAY - PATH] /home/1713/civ13-tdm/admin.log
[RELAY - PATH] /home/1713/civ13-rp/ooc.log
[RELAY - PATH] /home/1713/civ13-rp/admin.log
[RELAY - PATH] /home/1713/civ13-tdm/ooc.log
[RELAY - PATH] /home/1713/civ13-tdm/admin.log
[RELAY - PATH] /home/1713/civ13-rp/ooc.log
[RELAY - PATH] /home/1713/civ13-rp/admin.log
[RELAY - PATH] /home/1713/civ13-tdm/ooc.log
[RELAY - PATH] /home/1713/civ13-tdm/admin.log
[RELAY - PATH] /home/1713/civ13-rp/ooc.log
[RELAY - PATH] /home/1713/civ13-rp/admin.log
[RELAY - PATH] /home/1713/civ13-tdm/ooc.log
[RELAY - PATH] /home/1713/civ13-tdm/admin.log

Test code:

$ooc_relay = function ($guild, string $file_path, string $channel_id) use ($filesystem)
{
    /*
    if ($file = fopen($file_path, "r+")) {
        while (($fp = fgets($file, 4096)) !== false) {
            $fp = str_replace(PHP_EOL, "", $fp);
            if ($target_channel = $guild->channels->offsetGet($channel_id)) $target_channel->sendMessage($fp);
            else echo "[RELAY] Unable to find channel $target_channel" . PHP_EOL;
        }
        ftruncate($file, 0); //clear the file
        fclose($file);
    } else echo "[RELAY] Unable to open $file_path" . PHP_EOL;
    */

    echo '[RELAY - PATH] ' . $file_path . PHP_EOL;
    if ($target_channel = $guild->channels->offsetGet($channel_id)) {
        if ($file = $filesystem->file($file_path)) {
            $file->getContents()->then(function (string $contents) use ($file, $target_channel) {
                $promise = React\Async\async(function () use ($contents, $file, $target_channel) {
                    if ($contents) echo '[RELAY - CONTENTS] ' . $contents . PHP_EOL;
                    $lines = explode(PHP_EOL, $contents);
                    $promise2 = React\Async\async(function () use ($lines, $target_channel) {
                        foreach ($lines as $line) {
                            if ($line) {
                                echo '[RELAY - LINE] ' . $line . PHP_EOL;
                                $target_channel->sendMessage($line);
                            }
                        }
                        return;
                    })();
                    React\Async\await($promise2);
                })();
                $promise->then(function () use ($file) {
                    echo '[RELAY - TRUNCATE]' . PHP_EOL;
                    $file->putContents('');
                }, function (Exception $e) {
                    echo '[RELAY - ERROR] ' . $e->getMessage() . PHP_EOL;
                });
                React\Async\await($promise);
            })->done();
        } else echo "[RELAY - ERROR] Unable to open $file_path" . PHP_EOL;
    } else echo "[RELAY - ERROR] Unable to get channel $channel_id" . PHP_EOL;
};

I've also tried your originally posted code and a slight variation of it, but the results are the same.

if ($target_channel = $guild->channels->offsetGet($channel_id)) {
    if ($file = $filesystem->file($file_path)) {
        $file->getContents()->then(function (string $contents) use ($file, $target_channel) {
            var_dump($contents);
            $contents = explode(PHP_EOL, $contents);
            foreach ($contents as $line) {
                $target_channel->sendMessage($line);
            }
        })->then(
            function () use ($file) {
                $file->putContents('');
            }, function (Exception $e) {
                echo '[RELAY - getContents Error] ' . $e->getMessage() . PHP_EOL;
            }
        )->done();
    } else echo "[RELAY - ERROR] Unable to open $file_path" . PHP_EOL;
} else echo "[RELAY - ERROR] Unable to get channel $channel_id" . PHP_EOL;