From 0559bff00a6be2054194632c3543bf62af1fb56f Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Sat, 11 May 2019 19:03:57 +0100 Subject: Add the ability to replay messages from a replay log file. (No replay log file writing yet.) --- replay.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 replay.c (limited to 'replay.c') diff --git a/replay.c b/replay.c new file mode 100644 index 0000000..8c37d4c --- /dev/null +++ b/replay.c @@ -0,0 +1,231 @@ +#include "replay.h" + +// Return the unixtime timestamp at the start of a line, +// or -1 if a timestamp couldn't be extracted +int gettimestamp(char *str) { + int timestamp; + char timestr[TIMELEN]; + int count = 0; + + // Make sure we're starting with a digit + if (!isdigit(str[0])) { + return -1; + } + + // Extract each digit until we encounter a non-digit + for (int i = 0; i < TIMELEN; i++) { + if (isdigit(str[i])) { + timestr[count] = str[i]; + count++; + } + } + + timestr[count] = '\0'; // Null terminate + + timestamp = strtol(timestr, NULL, 10); // Convert resulting string to an integer, base 10 + + return timestamp; +} + +// Set 'str' to a string with the leading unixtime timestamp removed. +// Returns 1 on success, 0 on failure +int striptimestamp(char *str) { + char line[MAXCHAR]; + int count = 0; + + // Make sure we're starting with a digit + if (!isdigit(str[0])) { + return 0; + } + + // Skip over each digit until we encounter a non-digit, record the position + for (int i = 0; i < TIMELEN; i++) { + if (isdigit(str[i])) { + count++; + } + } + + // Skip over the space + count++; + + int count2 = 0; + + // Copy from the end of the digits (and the space) until the end of the string + for (size_t i = count; i < strlen(str); i++) { + line[count2] = str[i]; + count2++; + } + + strncpy(str, line, count2); + str[count2] = '\0'; + + return 1; +} + +// Take a string like: +// 1557592901 :foo!bar@baz PRIVMSG foo :hello world +// And convert it to: +// :foo!bar@baz PRIVMSG foo :[17:41:41] hello world +// Only inserts the formatted time for PRIVMSGs at the moment (and maybe only needs to!). +void formattime(char *str) { + // Extract the timestamp for conversion into [HH:MM:SS] + char timestr[TIMELEN]; + sprintf(timestr, "%d", gettimestamp(str)); // Convert int time to string time + struct tm tm; + strptime(timestr, "%s", &tm); + char timestampf[TIMELEN]; // Formatted timestamp + // Convert into [HH:MM:SS] + strftime(timestampf, TIMELEN, "[%H:%M:%S]", &tm); + printf("Formatted time string: '%s'.\n", timestampf); + + // Strip the original unixtimestamp + striptimestamp(str); + printf("Now got '%s' and '%s'.\n", timestampf, str); + + // Take note of the length + int len = strlen(str); + + // Find the start of the message if it's a PRIVMSG + char *ret; + int pos1; + if ((ret = strstr(str, "PRIVMSG")) != NULL) { + // Position within str of "PRIVMSG" + pos1 = ret - str; + printf("Found 'PRIVMSG' at position '%d' of '%s'.\n", pos1, str); + } else { + // If it's not a PRIVMSG, stop here + return; + } + + char *ret2; + int pos2; + // Find the start of the actual message within the PRIVMSG + if ((ret2 = strstr(ret, ":")) != NULL) { + // Position within ret of ":" + pos2 = ret2 - ret; + printf("Found ':' at position '%d' of '%s'.\n", pos2, ret); + } else { + // Didn't find the real message, weird. + return; + } + + // Position of start of PRIVMSG colon in original string + int realpos = pos1 + pos2 + 1; + + // Build the new formatted string + char newline[MAXCHAR]; + + // First bit (:foo!bar@baz PRIVMSG foo :) + for (int i = 0; i < realpos; i++) { + newline[i] = str[i]; + } + + // Second bit (:foo!bar@baz PRIVMSG foo :[HH:MM:SS]) + int j = 0; + for (int i = realpos; i < TIMELEN + realpos - 1; i++) { // -1 to avoid the null char from timestampf + newline[i] = timestampf[j]; + j++; + } + + // Insert a space after the formatted timestamp + newline[TIMELEN + realpos - 1] = ' '; + + // Final bit (the original PRIVMSG contents) + for (int i = TIMELEN + realpos; i < len + TIMELEN; i++) { + newline[i] = str[i - TIMELEN]; + } + + // Copy the whole thing back to str and null terminate + strncpy(str, newline, len + TIMELEN); + str[len + TIMELEN] = '\0'; + + printf("Ended up with relay string of: '%s'.\n", str); +} + +// Return the number of lines in the replay log since 'seconds' seconds ago, or -1 if there a problem. +int relaylines(int seconds) { + FILE *fp; + char str[MAXCHAR]; + char* filename = "replay.log"; + + int numlines = 0; + + fp = fopen(filename, "r"); + + if (fp == NULL) { + printf("error: could not open replay log '%s'.\n", filename); + exit(1); + } + + // Get the current time for comparison later + int timenow = (int)time(NULL); + + while (fgets(str, MAXCHAR, fp) != NULL) { + // Read the timestamp from each line + int timestamp = gettimestamp(str); + if (timestamp < 1) { + printf("Error reading timestamp from replay log file.\n"); + return -1; + } + + printf("Timestamp is: '%d'.\n", timestamp); + + // If the line is within range of the requested time, count it + if (timestamp > timenow - seconds) { + numlines++; + } + } + + fclose(fp); + return numlines; +} + +// Set 'str' to the line in the log with a timestamp of greater than 'seconds' +// seconds ago, plus however many lines 'linenum' is set to. +// Also modify the line to include a timestamp in the form "[HH:MM:SS]". +// Returns 1 on success, or 0 on failure. +// TODO - This is horribly inefficient since it re-reads the entire file each call, rewrite this! +int readrelayline(int seconds, int linenum, char *str) { + FILE *fp; + char line[MAXCHAR]; + char* filename = "replay.log"; + + int count = 0; + + fp = fopen(filename, "r"); + + if (fp == NULL) { + printf("error: could not open replay log '%s'.\n", filename); + return 1; + } + + // Get the current time for comparison later + int timenow = (int)time(NULL); + + while (fgets(line, MAXCHAR, fp) != NULL) { + printf("Read: '%s'.\n", line); + // Read the timestamp from each line + int timestamp = gettimestamp(line); + if (timestamp < 1) { + printf("Error reading timestamp from replay log file.\n"); + exit(1); + } + + // If the line is within range of the requested time... + if (timestamp > timenow - seconds) { + // ...and it is the current requested line then return it + if (count == linenum) { + // Insert our formatted [HH:MM:SS] timestamp into the message + formattime(line); + + strncpy(str, line, strlen(line)); + str[strlen(line)] = '\0'; + return 1; + } + count++; + } + } + + // If we got here something went wrong + return 0; +} -- cgit v1.2.3