public async Task ClearNotificationsAsync(WatchView watch, long uid) { using var search = dbFactory.CreateSearch(); var getRequest = new Func <string, SearchRequest>(t => new SearchRequest() { type = t, fields = "id,contentId", query = "contentId = @cid", order = "id_desc", limit = 1 }); var getValues = new Func <Dictionary <string, object> >(() => new Dictionary <string, object> { { "cid", watch.contentId } }); //Need the latest values for comment and activity. Don't care if message is a module //message, the id will still be valid for tracking purposes var messages = await search.SearchSingleType <MessageView>(uid, getRequest("message"), getValues()); var activity = await search.SearchSingleType <ActivityView>(uid, getRequest("activity"), getValues()); if (messages.Count > 0) { watch.lastCommentId = messages.First().id; } if (activity.Count > 0) { watch.lastActivityId = activity.First().id; } }
public async Task <LiveEventCachedData> LookupEventDataAsync(LiveEvent evnt) { var requests = GetSearchRequestsForEvents(new List <LiveEvent> { evnt }); using var search = dbFactory.CreateSearch(); var searchData = await search.SearchUnrestricted(requests); return(new LiveEventCachedData() { evnt = evnt, data = searchData.objects }); }
public GenericSearcher GetGenericSearcher() { var searcher = (GenericSearcher)dbFactory.CreateSearch(); spawnedSearches.Add(searcher); return(searcher); }
/// <summary> /// Using the given argument information, fill (as in actually mutate) the given list of argument values /// </summary> /// <param name="argumentInfos"></param> /// <param name="arglist"></param> /// <param name="existingArgs"></param> public void ParseArgs(ModuleSubcommandInfo subcommandInfo, string arglist, List <object> existingArgs) { using var search = dbFactory.CreateSearch(); var forcedFinal = false; foreach (var argInfo in subcommandInfo.Arguments) { //forcedFinal is specifically for "freeform" arguments, which MUST come at the end! It just //eats up the rest of the input if (forcedFinal) { throw new InvalidOperationException("No argument can come after a 'freeform' argument!"); } //Kind of wasteful, but just easier to always start with a clean slate after ripping args out arglist = arglist.Trim(); //Matcher function for generic regex matches. Useful for words, users, etc. Action <string, Action <Match> > genericMatch = (r, a) => { var regex = new Regex(r, RegexOptions.Compiled | RegexOptions.IgnoreCase); var match = regex.Match(arglist); //An argument of a given type must ALWAYS be a pure match. if (!match.Success) { throw new RequestException($"Parse error in argument '{argInfo?.name}', not of type '{argInfo?.type}'"); } //Get rid of the argument from the remaining arglist arglist = regex.Replace(arglist, ""); //Try to do whatever the user wanted (probably adding to existingArgs) try { a(match); } catch (Exception ex) { throw new RequestException($"{ex.Message} (Parse error in argument '{argInfo?.name}' of type '{argInfo?.type}')", ex); } }; //Parse arguments differently based on type switch (argInfo?.type) { case "user": genericMatch(@"^([0-9]+)(\([^\s]+\))?", m => { //Check if user exists! var uid = long.Parse(m.Groups[1].Value); //Yes this is BLOCKING, this entire module system is blocking because lua/etc //var users = userSource.SimpleSearchAsync(new UserSearch() { Ids = new List<long>{uid}}).Result; try { var user = search.GetById <UserView>(RequestType.user, uid); } catch (NotFoundException ex) { throw new RequestException($"User not found: {uid}", ex); } existingArgs.Add(uid); }); break; case "int": // This will throw an exception on error, not telling the user much genericMatch(@"^([0-9]+)(\([^\s]+\))?", m => { existingArgs.Add(long.Parse(m.Groups[1].Value)); }); break; case "word": genericMatch(@"^([^\s]+)", m => { existingArgs.Add(m.Groups[1].Value); }); break; case "freeform": existingArgs.Add(arglist); //Just append whatever is left forcedFinal = true; break; default: throw new InvalidOperationException($"Unknown argument type: {argInfo?.type} ({argInfo?.name})"); } } }
public async Task <Tuple <byte[], string> > GetFileAsync(string hash, GetFileModify modify) { if (modify.size > 0) { if (modify.size < 10) { throw new RequestException("Requested file size too small!"); } else if (modify.size <= 100) { modify.size = 10 * (modify.size / 10); } else if (modify.size <= 1000) { modify.size = 100 * (modify.size / 100); } else { throw new RequestException("Requested file size too large!"); } } //Go get that ONE file. This should return null if we can't read it... let's hope! //SPECIAL: the 0 file is ContentView?fileData = null; if (hash == Constants.DefaultHash) { fileData = new ContentView() { id = 0, literalType = FallbackMime, hash = hash, contentType = InternalContentType.file }; } else { //Doesn't matter who the requester is, ANY file with this hash is fine... what about deleted though? using var searcher = dbFactory.CreateSearch(); fileData = (await searcher.GetByField <ContentView>(RequestType.content, "hash", hash)).FirstOrDefault(); } if (fileData == null || fileData.deleted || fileData.contentType != InternalContentType.file) { throw new NotFoundException($"Couldn't find file data with hash {hash}"); } var thumbnailPath = GetThumbnailPath(hash, modify); var mimeType = fileData.literalType ?? ""; try { //This means they're requesting a MAIN data, just go get it if (string.IsNullOrEmpty(thumbnailPath)) { return(Tuple.Create(await GetMainDataAsync(hash), mimeType)); } else { return(Tuple.Create(await GetThumbnailAsync(hash, thumbnailPath, modify), mimeType)); } } catch (NotFoundException) { //This just means we CAN generate it! if (hash == Constants.DefaultHash && !string.IsNullOrWhiteSpace(config.DefaultImageFallback)) { logger.LogInformation($"Creating default image {hash} from base64 string given in config"); await SaveMainDataAsync(Convert.FromBase64String(config.DefaultImageFallback), hash, mimeType); return(await GetFileAsync(hash, modify)); } else { throw; } } }