function Searxng(matrixClient) { this.matrixClient = matrixClient; this.uploadMatrixClient = matrixClient; this.blacklistedImgRooms = []; this.blacklistedUimgRooms = []; this.cooldownImmuneUsers = []; this.roomCooldowns = new Map(); this.roomUserWhitelists = new Map(); this.searxngInstance = ""; this.searxngParameters = "enabled_engines=duckduckgo images__images,google images__images,bing images__images&disabled_engines=flickr__images,nyaa__images,qwant images__images,artic__images,deviantart__images,library of congress__images,unsplash__images,openverse__images" } Searxng.prototype.setUploadMatrixClient = function(uploadMatrixClient){ this.uploadMatrixClient = uploadMatrixClient; }; Searxng.prototype.setBlacklistedImgRooms = function(blacklistedImgRooms){ this.blacklistedImgRooms = blacklistedImgRooms; }; Searxng.prototype.setBlacklistedUimgRooms = function(blacklistedUimgRooms){ this.blacklistedUimgRooms = blacklistedUimgRooms; }; Searxng.prototype.setCooldownImmuneUsers = function(cooldownImmuneUsers){ this.cooldownImmuneUsers = cooldownImmuneUsers; }; Searxng.prototype.setRoomCooldowns = function(roomCooldowns){ this.roomCooldowns = roomCooldowns; }; Searxng.prototype.setRoomUserWhitelists = function(roomUserWhitelists){ this.roomUserWhitelists = roomUserWhitelists; }; Searxng.prototype.setSearxngInstance = function(searxngInstance){ this.searxngInstance = searxngInstance; }; Searxng.prototype.setSearxngParameters = function(searxngParameters){ this.searxngParameters = searxngParameters; }; const axios = require('axios'); const request = require('request'); const lastCooldownTimes = new Map(); const lastWarningTimes = new Map(); const imageSearchCmd = "!img" const unsafeImageSearchCmd = "!uimg" const searxngCommands = [ imageSearchCmd, unsafeImageSearchCmd ]; function sendImage(results, resultIndex, roomId, name, searchCommand, retryCount, matrixClient, uploadMatrixClient) { const resultObj = results[resultIndex] var imageURL; if (typeof resultObj === 'undefined' || resultObj == null || typeof resultObj.img_src === 'undefined' || resultObj.img_src == null || resultObj.img_src === '') { imageURL = ""; } else { imageURL = resultObj.img_src; if (imageURL.startsWith("//")) { imageURL = "https:" + imageURL; } } (async () => { try { const imageResponse = await axios.get(imageURL, { responseType: 'arraybuffer' }); var imageType = imageResponse.headers['content-type']; console.debug("[Search text: "+searchCommand+"], "+"[imageURL: "+imageURL+"], "+"[retryCount: "+retryCount+"]"+", [Engine: "+resultObj.engines[0]+"]"+", [content-type: "+imageType+"]"); const uploadResponse = await uploadMatrixClient.uploadContent(imageResponse.data, { rawResponse: false, type: imageType }); const matrixUrl = uploadResponse.content_uri; switch(imageType) { case "image/jpeg": case "image/jpg": name = name + ".jpg"; break; case "image/png": name = name + ".png"; break; case "image/gif": name = name + ".gif"; break; case "application/octet-stream": const urlImageType = imageURL.substring(imageURL.lastIndexOf(".")); switch(urlImageType) { case ".jpg": case ".jpeg": imageType = "image/jpg"; name = name + ".jpg"; break; case ".png": imageType = "image/png"; name = name + ".png"; break; case ".gif": imageType = "image/gif"; name = name + ".gif"; break; } break; } matrixClient.sendImageMessage(roomId, matrixUrl, {"mimetype": imageType}, name, null); matrixClient.sendTyping(roomId, false, 3000); } catch (error) { retryCount = retryCount + 1; resultIndex = resultIndex + 1; if (retryCount > 3 || resultIndex > (results.length - 1)) { matrixClient.sendNotice(roomId, "Error occurred during \"" + searchCommand + "\""); console.error("ERROR! [Search text: "+searchCommand+"], "+"[imageURL: "+imageURL+"], "+"[retryCount: "+retryCount+"]"+", [Engine: "+resultObj.engines[0]+"]"); console.error(error); } else { console.warn("Trying next result for '"+searchCommand+"'"); console.error(error); sendImage(results, resultIndex, roomId, name, searchCommand, retryCount, matrixClient, uploadMatrixClient); } } })(); } Searxng.prototype.handleMessage = function(event, room) { const messagePieces = event.getContent().body.split(" "); if (searxngCommands.indexOf(messagePieces[0]) > -1) { var cooldown = false; if (this.blacklistedImgRooms.indexOf(room.roomId) >=0) { this.matrixClient.sendNotice(room.roomId, "Image searching has been disabled in this room."); return; } else if (this.blacklistedUimgRooms.indexOf(room.roomId) >=0 && messagePieces[0] == unsafeImageSearchCmd) { this.matrixClient.sendNotice(room.roomId, "Image searching without safesearch is disabled in this room."); return; } else if (!room.currentState.maySendMessage(this.matrixClient.getUserId())) { console.warn("Don't have permission to post in room "+room.roomId); return; } else if (this.roomUserWhitelists.has(room.roomId) && this.roomUserWhitelists.get(room.roomId).indexOf(event.getSender()) < 0) { const lastRoomWarningTime = lastWarningTimes.get(room.roomId); const currentTime = Date.now(); if (isNaN(lastRoomWarningTime) || (currentTime - lastRoomWarningTime) > 10000) { this.matrixClient.sendNotice(room.roomId, "You are not on the whitelist for this room."); lastWarningTimes.set(room.roomId, currentTime); } return; } else if (this.cooldownImmuneUsers.indexOf(event.getSender()) < 0) { const roomCooldown = this.roomCooldowns.get(room.roomId); if (roomCooldown != undefined) { const currentTime = Date.now(); const lastRoomCooldownTime = lastCooldownTimes.get(room.roomId); if (lastRoomCooldownTime != undefined) { const timeSinceLastCooldown = currentTime - lastRoomCooldownTime; if (!isNaN(timeSinceLastCooldown) && (currentTime - lastRoomCooldownTime) < roomCooldown) { const lastRoomWarningTime = lastWarningTimes.get(room.roomId); if (isNaN(lastRoomWarningTime) || lastRoomWarningTime != lastRoomCooldownTime) { this.matrixClient.sendNotice(room.roomId, "This room has a mandatory cooldown between image commands. Try again later."); lastWarningTimes.set(room.roomId, lastRoomCooldownTime); } return; } } lastCooldownTimes.set(room.roomId, currentTime); cooldown = true; } } try { var toSearch; var resultIndex = 0; if (messagePieces.length <= 1) { this.matrixClient.sendNotice(room.roomId, "No search terms provided"); return; } else if (messagePieces.length == 2) { toSearch = messagePieces[1].trim(); } else { if (isNaN(messagePieces[1])) { toSearch = event.getContent().body.substr(messagePieces[0].length+1); } else { resultIndex = Number(messagePieces[1]) - 1; if (resultIndex < 0) { this.matrixClient.sendNotice(room.roomId, "Invalid result index given."); return; } toSearch = event.getContent().body.substr(messagePieces[0].length+messagePieces[1].length+2); } } const searchURL = this.searxngInstance + "/search?category_images=1&pageno=1&time_range=&format=json&"+this.searxngParameters+"&safesearch="+(messagePieces[0] == unsafeImageSearchCmd ? "0" : "1")+"&q=" + toSearch; this.matrixClient.sendTyping(room.roomId, true, 3000); const matrixClient = this.matrixClient; const uploadMatrixClient = this.uploadMatrixClient; request(searchURL, function (error, response, body) { try { if (error == null || error == "") { const bodyObj = JSON.parse(body); const results = bodyObj.results; if (typeof results === 'undefined' || results == null || results.length <= 0) { matrixClient.sendNotice(room.roomId, "No results found for \""+toSearch+"\""); return; } else if (resultIndex > (results.length - 1)) { matrixClient.sendNotice(room.roomId, "No result found for \""+toSearch+"\" at position "+(resultIndex+1)); return; } sendImage(results, resultIndex, room.roomId, toSearch, event.getContent().body, 0, matrixClient, uploadMatrixClient); } else { matrixClient.sendNotice(room.roomId, "Error occurred during \"" + event.getContent().body + "\""); console.error(error); } } catch (error) { matrixClient.sendNotice(room.roomId, "Error occurred during \"" + event.getContent().body + "\""); console.error(error); } if (cooldown) { lastCooldownTimes.set(room.roomId, Date.now()); } }); } catch (error) { this.matrixClient.sendNotice(room.roomId, "Error occurred during \"" + event.getContent().body + "\""); console.error(error); } } }; Searxng.prototype.startUploadMatrixClient = function() { (async () => { this.uploadMatrixClient.startClient({initialSyncLimit: 1}); })(); } Searxng.prototype.getFormattedHelp = function() { return "\ !img [image_search_text] - Return top result for image
\ !img [n] [image_search_text] - Return nth result for image. [n] must be a positive number (example: !img 3 cat memes)
\ !uimg - Same as !img but without safesearch"; } Searxng.prototype.getHelp = function() { return "!img [image_search_text]\" - Return top result for image\r\n\ \"!img [n] [image_search_text]\" - Return nth result for image. [n] must be a positive number (example: '!img 3 cat memes')\r\n\ \"!uimg\" - Same as !img but without safesearch"; } module.exports = Searxng;