#wip: ScrapDSL and Drivers.

This commit is contained in:
KyMAN 2026-07-01 07:46:29 +02:00
parent 922f995022
commit bc0a86900a
30 changed files with 1422 additions and 122 deletions

View File

@ -200,24 +200,9 @@
"AnP_HTTPAbstracts_end" : null, "AnP_HTTPAbstracts_end" : null,
"AnP_WebSocketsServersManager_start" : null, "AnP_WebSocketsServersManager_start" : null,
"default_web_sockets_servers" : {
"anp" : {
"type" : "WebSocketServerDriver",
"host" : "",
"port" : 18765
}
},
"AnP_WebSocketsServersManager_end" : null, "AnP_WebSocketsServersManager_end" : null,
"AnP_HTTPServersManager_start" : null, "AnP_HTTPServersManager_start" : null,
"default_http_servers" : {
"anp" : {
"type" : "HTTPServerDriver",
"type2" : "HTTPSocketServerDriver",
"host" : "",
"port" : 18000
}
},
"AnP_HTTPServersManager_end" : null, "AnP_HTTPServersManager_end" : null,
"AnP_PseudoLoRAsManager_start" : null, "AnP_PseudoLoRAsManager_start" : null,
@ -225,34 +210,6 @@
"AnP_PseudoLoRAsManager_end" : null, "AnP_PseudoLoRAsManager_end" : null,
"AnP_AIInterpretersManager_start" : null, "AnP_AIInterpretersManager_start" : null,
"default_ai_interpreters" : {
"anp_titles" : {
"type" : "OllamaDriver",
"url" : "http://localhost:11434/api/generate/",
"model" : "gemma3:1b",
"pool" : "anp",
"format" : {
"type": "array",
"items": {
"type": "boolean"
}
},
"stream" : false,
"think" : false,
"temperature" : 0.0,
"allow_contexts" : true
},
"anp_responses" : {
"type" : "OllamaDriver",
"url" : "http://localhost:11434/api/generate/",
"model" : "gemma4:e4b",
"stream" : true,
"pool" : "anp",
"think" : false,
"temperature" : 0.7,
"allow_contexts" : true
}
},
"AnP_AIInterpretersManager_end" : null, "AnP_AIInterpretersManager_end" : null,
"AnP_TitlesManager_start" : null, "AnP_TitlesManager_start" : null,

View File

@ -61,7 +61,15 @@
"AnP_CloneController_start" : null, "AnP_CloneController_start" : null,
"anp_clone_controller_file_cloned" : "El archivo '{file}' ha sido clonado desde '{from_path}' a '{to_path}'.", "anp_clone_controller_file_cloned" : "El archivo '{file}' ha sido clonado desde '{from_path}' a '{to_path}'.",
"anp_clone_controller_file_deleted" : "El archivo '{file}' ha sido eliminado de '{to_path}'.", "anp_clone_controller_file_deleted" : "El archivo '{file}' ha sido eliminado de '{to_path}'.",
"AnP_CloneController_end" : null "AnP_CloneController_end" : null,
"AnP_BrowserAbstract_start" : null,
"anp_browser_action_exception" : "Hubo una excepción al intentar ejecutar la acción '{i}' de tipo '{action}' en el navegador '{key}'.",
"anp_browser_action_done" : "La acción '{i}' de tipo '{action}' ha sido ejecutada correctamente en el navegador '{key}'.",
"anp_browser_action_not_done" : "La acción '{i}' de tipo '{action}' no ha sido ejecutada correctamente en el navegador '{key}'.",
"anp_browser_action_not_supported" : "La acción '{i}' de tipo '{action}' no es compatible con el navegador '{key}'.",
"anp_browser_action_not_action" : "El elemento '{i}' no es una acción válida en el navegador '{key}'.",
"AnP_BrowserAbstract_end" : null
} }
} }

View File

@ -56,6 +56,76 @@ export const AIChatComponent = (function(){
return item; return item;
}; };
/**
* @param {!string} id
* @param {!string} type
* @param {?string} [content = ""]
* @param {?string} [mode = "waiting"]
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @returns {Array.<any|null>}
* @access private
*/
const build_message = (id, type, content = "", mode = "waiting", inputs = null) => {
/** @type {[string, string, string]} */
const [html, raw_html, md] = format(content),
/** @type {number} */
date_from = Common.get_value("date_from", inputs) || Date.now();
return Fieldset({
class : "message",
data_id : id,
data_type : type,
data_ok : true,
data_status : mode,
data_done : mode == "done",
data_mode : "html"
}, [
anp.components.i18n(type, "legend"),
Div({class : "message-box html-content"}, html),
Pre({class : "message-box raw-html-content"}, raw_html),
Pre({class : "message-box md-content"}, md),
Nav({class : "view buttons"}, [
anp.components.button("html", set_view),
anp.components.button("raw_html", set_view),
anp.components.button("md", set_view),
]),
UL({class : "data"}, [
build_data_icon("status", mode),
build_data_item("ok"),
build_data_item("done"),
build_data_item("date_from", date_from),
build_data_item("date_to", Common.get_value("date_to", inputs) || date_from),
build_data_item("time", 0),
build_data_item("length", content.length),
build_data_item("response_tokens", content.replace(/(?:[^a-z0-9]+|[\r\n]+)+/gi, " ").trim().split(" ").length)
])
]);
};
/**
* @param {?string} session
* @param {!string} name
* @returns {Array.<any|null>}
* @access private
*/
const build_new_conversation = (session, name) => {
const session_key = session + "-" + name;
/** @type {string} */
conversation = anp.unique_keys.get();
cache[cache_key].conversations[conversation] = [];
document.querySelectorAll(".aichat[")
return Article({
class : "messages",
data_conversation : conversation,
data_selected : true
});
};
/** /**
* @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null] * @param {?(Object.<string, any|null>|Array.<any|null>)} [inputs = null]
* @returns {Array.<any|null>} * @returns {Array.<any|null>}
@ -64,20 +134,49 @@ export const AIChatComponent = (function(){
this.build = (inputs = null) => { this.build = (inputs = null) => {
/** @type {string} */ /** @type {string} */
const name = Common.get_value("name", inputs, "aichat"); const name = Common.get_value("name", inputs, "aichat"),
/** @type {string|null} */
session = Common.get_value("session", inputs, null),
/** @type {string} */
cache_key = session + "-" + name;
if(session){
cache[cache_key] = {
selected : conversations[conversations.length - 1],
conversations : conversations.reduce((dictionary, conversation) => {
dictionary[conversation] = [];
return dictionary;
}, {})
};
Common.preload("[data-name=" + name + "]", (chats, asynchronous, ok) => {
if(ok){
const data = Common.data_decode(anp.sessions.get_value(session, name));
if(data){
cache[cache_key] = data;
return;
};
Common.HTML(chats.querySelector(".conversations"), build_new_conversation(session, name));
};
});
};
return Fieldset({ return Fieldset({
class : "aichat", class : "aichat",
data_name : Common.get_value("name", inputs, "aichat") data_name : name,
data_session : session
}, [ }, [
anp.components.i18n(name, "legend"), anp.components.i18n(name, "legend"),
Section({class : "conversations"}, [ Section({class : "conversations"}, conversations.map(conversation => Article({
Article({ class : "messages",
class : "messages", data_conversation : conversation,
data_conversation : anp.unique_keys.get(), data_selected : true
data_selected : true }))),
})
]),
Form({ Form({
method : "post", method : "post",
action : "#", action : "#",
@ -172,52 +271,6 @@ export const AIChatComponent = (function(){
return [html, html.replace(/</g, "&lt;").replace(/>/g, "&gt;"), content]; return [html, html.replace(/</g, "&lt;").replace(/>/g, "&gt;"), content];
}; };
/**
* @param {!string} id
* @param {!string} type
* @param {?string} [content = ""]
* @param {?string} [mode = "waiting"]
* @returns {HTMLFielsetElement}
* @access private
*/
const build_message = (id, type, content = "", mode = "waiting") => {
/** @type {[string, string, string]} */
const [html, raw_html, md] = format(content),
/** @type {number} */
date = Date.now();
return Fieldset({
class : "message",
data_id : id,
data_type : type,
data_ok : true,
data_status : mode,
data_done : mode == "done",
data_mode : "html"
}, [
anp.components.i18n(type, "legend"),
Div({class : "message-box html-content"}, html),
Pre({class : "message-box raw-html-content"}, raw_html),
Pre({class : "message-box md-content"}, md),
Nav({class : "view buttons"}, [
anp.components.button("html", set_view),
anp.components.button("raw_html", set_view),
anp.components.button("md", set_view),
]),
UL({class : "data"}, [
build_data_icon("status", mode),
build_data_item("ok"),
build_data_item("done"),
build_data_item("date_from", date),
build_data_item("date_to", date),
build_data_item("time", 0),
build_data_item("length", content.length),
build_data_item("response_tokens", content.replace(/(?:[^a-z0-9]+|[\r\n]+)+/gi, " ").trim().split(" ").length)
])
]);
};
/** /**
* *
* @param {!HTMLElement} item * @param {!HTMLElement} item
@ -238,8 +291,6 @@ export const AIChatComponent = (function(){
if(text){ if(text){
console.log([item, item.querySelector("[name=message]"), item.querySelector("[name=message]").parentNode.parentNode.getAttribute("data-connection")]);
/** @type {string} */ /** @type {string} */
const data_id = anp.unique_keys.get(), const data_id = anp.unique_keys.get(),
/** @type {[string, string, string]} */ /** @type {[string, string, string]} */

View File

@ -28,7 +28,9 @@ export const SessionsManager = (function(){
/** @type {SessionsManager} */ /** @type {SessionsManager} */
const self = this, const self = this,
/** @type {Object.<number, SessionModel>} */ /** @type {Object.<number, SessionModel>} */
sessions = {}; sessions = {},
/** @type {Object.<string, number>} */
keys = {};
/** /**
* @returns {void} * @returns {void}
@ -36,17 +38,31 @@ export const SessionsManager = (function(){
*/ */
const constructor = () => {}; const constructor = () => {};
/**
* @param {!(string|number)} id
* @returns {SessionModel|null}
* @access public
*/
const get = id => sessions[id] || sessions[keys[id]] || null;
/** /**
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs * @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
* @return {number} * @return {number|null}
* @access public * @access public
*/ */
this.set = inputs => { this.set = inputs => {
const key = Common.get_value("key", inputs, null);
if(keys[key])
return null;
/** @type {number} */ /** @type {number} */
let i; let i;
while(sessions[i = Math.random() * (1 << 28) | 0]); while(sessions[i = Math.random() * (1 << 28) | 0]);
if(key)
keys[key] = i;
return (sessions[i] = new SessionModel(i, inputs)).i; return (sessions[i] = new SessionModel(i, inputs)).i;
}; };
@ -57,16 +73,24 @@ export const SessionsManager = (function(){
* @returns {boolean} * @returns {boolean}
* @access public * @access public
*/ */
this.check_permissions = (ids, permissions) => Common.get_array(ids).some(id => ( this.check_permissions = (ids, permissions) => Common.get_array(ids).some(id => {
sessions[id] && sessions[id].check_permissions(permissions)
)); /** @type {SessionModel|null} */
const session = get(id);
return session && session.check_permissions(permissions)
});
/** /**
* @param {!number} id * @param {!(string|number)} id
* @returns {boolean} * @returns {boolean}
* @access public * @access public
*/ */
this.close = id => { this.close = id => {
if(keys[id]){
id = keys[id];
delete keys[id];
};
if(sessions[id]){ if(sessions[id]){
delete sessions[id]; delete sessions[id];
return true; return true;
@ -74,6 +98,49 @@ export const SessionsManager = (function(){
return false; return false;
}; };
/**
* @param {!(string|number)} id
* @param {!string} key
* @returns {boolean}
* @access public
*/
this.has_value = (id, key) => {
/** @type {SessionModel|null} */
const session = get(id);
return session ? session.has(key) : false;
};
/**
* @param {!(string|number)} id
* @param {!string} key
* @returns {any|null}
* @access public
*/
this.get_value = (id, key) => {
/** @type {SessionModel|null} */
const session = get(id);
return session ? session.get(key) : null;
};
/**
* @param {!(string|number)} id
* @param {!string} key
* @param {?any} [value = null]
* @returns {boolean}
* @access public
*/
this.set_value = (id, key, value) => {
/** @type {SessionModel|null} */
const session = get(id);
return session ? session.set(key, value) : null;
};
constructor(); constructor();
}; };

View File

@ -0,0 +1,196 @@
"use strict";
import {Event} from "../Application/Event.ecma.js";
import {Common} from "../Utils/Common.ecma.js";
/**
* @class AIChatMessageModel
* @constructor
* @param {!AIChatConversationModel} conversation
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
* @return {void}
* @access public
* @static
*/
export const AIChatMessageModel = (function(){
/**
* @constructs AIChatMessageModel
* @param {!AIChatConversationModel} conversation
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
* @return {void}
* @access private
* @static
*/
const AIChatMessageModel = function(conversation, inputs){
/** @type {AIChatConversationModel} */
this.conversation = conversation;
/** @type {string} */
this.status = Common.get_value("status", inputs, "unloaded");
/** @type {string|null} */
this.id = Common.get_value("id", inputs);
/** @type {string} */
this.role = Common.get_value("role", inputs, "unknown");
/** @type {boolean} */
this.ok = Common.get_value("ok", inputs, false);
/** @type {boolean} */
this.done = Common.get_value("done", inputs, false);
/** @type {string} */
this.message = (Common.get_value("message", inputs, ""));
/** @type {number} */
this.tokens = Common.get_value("tokens", inputs, 0);
/** @type {number} */
this.date_from = Common.get_value("date_from", inputs, Date.now());
/** @type {number} */
this.date_to = Common.get_value("date_to", inputs, Date.now());
};
return AIChatMessageModel;
})();
/**
* @class AIChatConversationModel
* @constructor
* @param {!AIChatModel} chat
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
* @return {void}
* @access public
* @static
*/
export const AIChatConversationModel = (function(){
/**
* @constructs AIChatConversationModel
* @param {!AIChatModel} chat
* @param {!(Object.<string, any|null>|Array.<any|null>)} inputs
* @return {void}
* @access private
* @static
*/
const AIChatConversationModel = function(chat, inputs){
/** @type {AIChatModel} */
this.chat = chat;
/** @type {string} */
this.id = Common.get_value("id", inputs);
/** @type {Array.<AIChatMessageModel>} */
this.messages = Common.get_value("messages", inputs, []).map(message => new AIChatMessageModel(this, message));
/** @type {boolean} */
this.selected = Common.get_value("selected", inputs, true);
};
return AIChatConversationModel;
})();
/**
* @class AIChatModel
* @constructor
* @return {void}
* @access private
* @static
*/
export const AIChatModel = (function(){
/**
* @constructs AIChatModel
* @return {void}
* @access private
* @static
*/
const AIChatModel = function(){
/** @type {AIChatModel} */
const self = this;
/** @type {Array.<AIChatConversationModel>} */
this.conversations = [];
/** @type {AIChatConversationModel|null} */
this.selected_conversation = null;
/** @type {Event} */
this.on_change = new Event();
/**
* @returns {void}
* @access private
*/
const constructor = () => {
self.on_change.add(update_changes);
};
/**
* @param {!AIChatMessageModel} model
* @returns {void}
* @access private
*/
const update_changes = model => {
/** @type {HTMLElement|null} */
const message = document.querySelector(".aichat [data-conversation='" + model.conversation.id + "'] .aichat-message[data-id='" + model.id + "']");
};
/**
* @param {!string} conversation
* @param {!string} id
* @returns {AIChatMessageModel|null}
* @access private
*/
const get_message = (conversation, id) => {
/** @type {AIChatConversationModel|null} */
const conversation_model = self.conversations.find(model => model.id === conversation);
if(conversation_model)
return conversation_model.messages.find(model => model.id === id) || null;
return null;
};
/**
* @param {!string} message
* @param {!boolean} [done = false]
* @returns {boolean}
* @access public
*/
this.update = (conversation, id, message, done = false) => {
/** @type {AIChatMessageModel|null} */
let model = get_message(conversation, id);
if(!model)
return false;
model.message += message;
model.tokens ++;
model.done = done;
conversation_model.chat.on_change.execute(model);
return true;
};
/**
* @param {!string} status
* @returns {boolean}
* @access public
*/
this.set_status = status => {
/** @type {AIChatMessageModel|null} */
let model = get_message(conversation, id);
if(!model)
return false;
model.status = status;
conversation_model.chat.on_change.execute(model);
return true;
};
constructor();
};
return AIChatModel;
})();

View File

@ -23,13 +23,19 @@ export const SessionModel = (function(){
const SessionModel = function(i, inputs){ const SessionModel = function(i, inputs){
/** @type {SessionModel} */ /** @type {SessionModel} */
const self = this; const self = this,
/** @type {Object.<string, any|null>} */
data = {};
/** @type {number|null} */ /** @type {number|null} */
let id = null, let id = null,
/** @type {Array.<string>} */ /** @type {Array.<string>} */
permissions = [], permissions = [],
/** @type {number} */ /** @type {number} */
last_check = 0; last_check = 0,
/** @type {string|null} */
cookie_key = null,
/** @type {integer|null} */
timeout = null;
/** @type {string|null} */ /** @type {string|null} */
this.nick = null; this.nick = null;
@ -43,11 +49,36 @@ export const SessionModel = (function(){
* @access private * @access private
*/ */
const constructor = () => { const constructor = () => {
id = Common.get_value("id", inputs, null);
permissions = Common.get_value("permissions", inputs, []); /** @type {string|null} */
self.nick = Common.get_value("nick", inputs, null); const cookie = anp.cookies.get(cookie_key = Common.get_value("cookie", inputs, null));
self.type = Common.get_value("type", inputs, self.type);
if(cookie){
/** @type {string} */
let raw_permissions,
/** @type {string} */
raw_data,
/** @type {string} */
raw_type,
/** @type {string} */
raw_id;
[raw_id, self.nick, raw_type, raw_permissions, raw_data] = cookie.split("|");
permissions = raw_permissions.split(",");
Object.assign(Common.data_encode(raw_data)).forEach(([key, value]) => self.set(key, value, false));
self.type = Number(raw_type) || SessionModel.LOCAL;
id = Number(raw_id) || null;
}else{
id = Common.get_value("id", inputs, null);
permissions = Common.get_value("permissions", inputs, []);
self.nick = Common.get_value("nick", inputs, null);
self.type = Number(Common.get_value("type", inputs, self.type)) || SessionModel.LOCAL;
};
last_check = Date.now(); last_check = Date.now();
timeout = Common.get_value(["session_timeout", "timeout"], inputs, 300000);
}; };
/** /**
@ -57,6 +88,57 @@ export const SessionModel = (function(){
*/ */
this.check_permissions = permissions => permissions.some(self.permissions.includes); this.check_permissions = permissions => permissions.some(self.permissions.includes);
/**
* @param {!string} key
* @returns {boolean}
* @access public
*/
this.has = key => data[key] !== undefined;
/**
* @param {!string} key
* @param {?any} [value = null]
* @param {!boolean} [overwrite = true]
* @returns {boolean}
* @access public
*/
this.set = (key, value = null, overwrite = true) => {
if(Check.is_key(key) && (overwrite || !self.has(key))){
data[key] = value;
return true;
};
return false;
};
/**
* @param {!string} key
* @returns {any|null}
* @access public
*/
this.get = key => self.has(key) ? data[key] : null;
/**
* @returns {boolean}
* @access public
*/
this.save = () => {
if(cookie_key){
anp.cookies.set(cookie_key, Common.data_encode([
id,
self.nick,
self.type,
permissions.join(","),
Common.data_encode(data)
]).join("|"), {
overwrite : true,
expires : new Date(Date.now() + timeout),
path : "/"
});
return true;
};
return false;
};
constructor(); constructor();
}; };

View File

@ -32,6 +32,7 @@
"raw_html" : "Código HTML", "raw_html" : "Código HTML",
"minimum" : "Mínimo", "minimum" : "Mínimo",
"maximum" : "Máximo", "maximum" : "Máximo",
"ai" : "IA" "ai" : "IA",
"anpbot" : "AnPBot"
} }
} }

View File

@ -0,0 +1,181 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Optional, Self, Any, Callable, Sequence, TypeVar
from abc import abstractmethod
from AnP.Interfaces.Application.AnPInterface import AnPInterface
from AnP.DSLs.ScrapDSL import (
Execute, Action, Open, Close, Switch, Wait, Do, Go, Get, ForEach, And, Or, Not,
If, While, DoWhile, Download
)
from AnP.Utils.Common import Common
from AnP.Abstracts.ModelAbstract import ModelAbstract
T = TypeVar("T")
class BrowserAbstract(ModelAbstract):
def __init__(self:Self, anp:AnPInterface, key:str, TabType:T, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
self.anp:AnPInterface = anp
self.key:str = key
self.working:bool = True
self.tabs:dict[str, T] = {}
self.tab_selected:str = ""
self.human_time:float|int|Sequence[float|int] = Common.get_value("human_time", inputs, 0.0)
self.actions:dict[Action, Callable[[Action, dict[str, Any|None], Optional[Any]], None]] = {
Execute : self.execute,
Open : self.open,
Close : self.close,
Switch : self.switch,
Wait : self.wait,
Do : self.do,
Go : self.go,
Get : self.get,
ForEach : self.foreach,
And : self._and,
Or : self._or,
Not : self._not
}
def start(self:Self) -> None:
self.working = True
def close(self:Self) -> None:
self.working = False
self.tabs.clear()
self.tab_selected = ""
def is_working(self:Self) -> bool:
return self.working and self.anp.working()
@abstractmethod
def run(self:Self, actions:Sequence[Action], shared:dict[str, Any|None] = {}) -> dict[str, Any|None]:pass
@abstractmethod
def execute(self:Self, action:Execute, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def open(self:Self, action:Open, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def close(self:Self, action:Close, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def switch(self:Self, action:Switch, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
def wait(self:Self, action:Wait, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
if action.is_fixed:
self.anp.wait(action.time)
else:
if action.is_range:
self.anp.wait(Common.random_number(*action.time[0]))
else:
self.anp.wait(Common.random(*action.time))
action.done = True
def do(self:Self, action:Do, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
subaction:Action
i:int
action.done = True
for i, subaction in enumerate(action.actions):
if not self.anp.working():
action.done = False
return
if isinstance(subaction, Action):
subaction.done = False
subaction.exception = None
if subaction.__class__ in self.actions:
try:
ok:bool
self.actions[subaction.__class__](subaction, shared, last_item)
ok = subaction.done and subaction.exception is None
if not ok and not subaction.ignore_fail:
if subaction.exception is None:
subaction.print_errors and self.anp.print("warning", "anp_browser_action_not_done", {
"key" : self.key,
"action": subaction.__class__.__name__,
"i" : i
})
else:
self.anp.exception(subaction.exception, "anp_browser_action_exception", {
"key" : self.key,
"action": subaction.__class__.__name__,
"i" : i
})
action.done = False
break
if subaction.wait:
self.wait(Wait(self.human_time, True))
ok and subaction.print_ok and self.anp.print("info", "anp_browser_action_done", {
"key" : self.key,
"action": subaction.__class__.__name__,
"i" : i
})
except Exception as exception:
subaction.exception = exception
subaction.print_errors and self.anp.exception(exception, "anp_browser_action_exception", {
"key" : self.key,
"action": subaction.__class__.__name__,
"i" : i
})
if not subaction.ignore_fail:
action.done = False
break
else:
subaction.print_errors and self.anp.print("warning", "anp_browser_action_not_supported", {
"key" : self.key,
"action": subaction.__class__.__name__,
"i" : i
})
if not subaction.ignore_fail:
action.done = False
break
else:
self.anp.print("warning", "anp_browser_action_not_action", {
"key" : self.key,
"i" : i
})
action.done = False
break
@abstractmethod
def go(self:Self, action:Go, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def get(self:Self, action:Get, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def foreach(self:Self, action:ForEach, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def _and(self:Self, action:And, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:pass
@abstractmethod
def _or(self:Self, action:Or, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:pass
@abstractmethod
def _not(self:Self, action:Not, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:pass
@abstractmethod
def _if(self:Self, action:If, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def _while(self:Self, action:While, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def do_while(self:Self, action:DoWhile, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def download(self:Self, action:Download, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass

View File

@ -24,6 +24,7 @@ from AnP.Managers.WebSocketServersManager import WebSocketServersManager
from AnP.Managers.HTTPServersManager import HTTPServersManager from AnP.Managers.HTTPServersManager import HTTPServersManager
from AnP.Managers.PseudoLoRAsManager import PseudoLoRAsManager from AnP.Managers.PseudoLoRAsManager import PseudoLoRAsManager
from AnP.Managers.AIInterpretersManager import AIInterpretersManager from AnP.Managers.AIInterpretersManager import AIInterpretersManager
from AnP.Managers.BrowsersManager import BrowsersManager
from AnP.Utils.Common import Common from AnP.Utils.Common import Common
from AnP.Utils.Patterns import RE from AnP.Utils.Patterns import RE
@ -62,6 +63,7 @@ class AnP:
self.http_servers:HTTPServersManager = HTTPServersManager(self) self.http_servers:HTTPServersManager = HTTPServersManager(self)
self.pseudoloras:PseudoLoRAsManager = PseudoLoRAsManager(self) self.pseudoloras:PseudoLoRAsManager = PseudoLoRAsManager(self)
self.ai_interpreters:AIInterpretersManager = AIInterpretersManager(self) self.ai_interpreters:AIInterpretersManager = AIInterpretersManager(self)
self.browsers:BrowsersManager = BrowsersManager(self)
def update(self:Self) -> None: def update(self:Self) -> None:
self.settings.update() self.settings.update()
@ -81,6 +83,7 @@ class AnP:
self.http_servers.update() self.http_servers.update()
self.pseudoloras.update() self.pseudoloras.update()
self.ai_interpreters.update() self.ai_interpreters.update()
self.browsers.update()
def reset(self:Self) -> None: def reset(self:Self) -> None:
self.settings.reset() self.settings.reset()
@ -98,6 +101,7 @@ class AnP:
self.web_socket_servers.reset() self.web_socket_servers.reset()
self.http_servers.reset() self.http_servers.reset()
self.ai_interpreters.reset() self.ai_interpreters.reset()
self.browsers.reset()
def close(self:Self) -> None: def close(self:Self) -> None:
self.__working = False self.__working = False
@ -105,6 +109,7 @@ class AnP:
self.ai_interpreters.close() self.ai_interpreters.close()
self.web_socket_servers.close() self.web_socket_servers.close()
self.http_servers.close() self.http_servers.close()
self.browsers.close()
def working(self:Self) -> bool: def working(self:Self) -> bool:
return self.__working return self.__working

View File

@ -0,0 +1,2 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

166
Python/AnP/DSLs/ScrapDSL.py Normal file
View File

@ -0,0 +1,166 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self, Sequence, Any, Optional, Callable
from re import Pattern as REPattern, Match as REMatch
class Action:
def __init__(self:Self) -> None:
self.exception:Exception|None = None
self.print_errors:bool = True
self.print_ok:bool = False
self.done:bool = False
self.wait:bool = True
class Execute(Action):
def __init__(self:Self, callback:Callable[[dict[str, Any|None]], None]) -> None:
super().__init__()
self.callback:Callable[[dict[str, Any|None]], None] = callback
class Open(Action):
def __init__(self:Self, key:str, url:Optional[str] = None) -> None:
super().__init__()
self.key:str = key
self.url:str|None = url
class Close(Action):
def __init__(self:Self, key:Optional[str] = None) -> None:
super().__init__()
self.key:str|None = key
class Switch(Action):
def __init__(self:Self, key:str) -> None:
super().__init__()
self.key:str = key
class Wait(Action):
def __init__(self:Self, time:float|int|Sequence[float|int], is_range:bool = True) -> None:
super().__init__()
self.time:float|int|Sequence[float|int] = time
self.is_fixed:bool = isinstance(time, (float, int))
self.is_range:bool = is_range
class Do(Action):
def __init__(self:Self, *actions:Action) -> None:
super().__init__()
self.actions:tuple[Action, ...] = actions
class Go(Action):
def __init__(self:Self, url:str, tab:Optional[str] = None) -> None:
super().__init__()
self.url:str = url
self.tab:str|None = tab
class Selector(Action):
def __init__(self:Self,
selector:str,
multiple:bool = False
) -> None:
super().__init__()
self.selector:str = selector
self.multiple:bool = multiple
class XPath(Selector):pass
class CSS(Selector):pass
class ID(Selector):pass
class Class(Selector):pass
class LinkText(Selector):pass
class Name(Selector):pass
class Get(Action):
def __init__(self:Self,
key:str,
attribute:str = "CDATA",
selector:Optional[Selector] = None,
reset:bool = False,
pattern:tuple[Optional[REPattern], str|Callable[[REMatch], str]] = None
) -> None:
super().__init__()
self.key:str = key
self.attribute:str = attribute
self.selector:Selector|None = selector
self.reset:bool = reset
self.pattern:REPattern|None = None
self.matches:str|Callable[[REMatch], str]|None = None
if pattern is not None:
self.pattern, self.matches = pattern
class ForEach(Action):
def __init__(self:Self,
items:Selector|Sequence[Any|None],
actions:Sequence[Action],
callback:Optional[Callable[[Any|None], None]] = None
) -> None:
super().__init__()
self.items:Selector|Sequence[Any|None] = items
self.actions:Sequence[Action] = actions
self.callback:Callable[[Any|None], None]|None = callback
class Condition(Action):
def __init__(self:Self, conditions:Sequence[Callable[[dict[str, Any|None]], bool]]|Selector|Self) -> None:
super().__init__()
self.conditions:Sequence[Callable[[dict[str, Any|None]], bool]|Selector|Condition|bool] = conditions
self.ok:bool = False
class And(Condition):pass
class Or(Condition):pass
class Not(Action):
def __init__(self:Self, condition:Callable[[dict[str, Any|None]], bool]|Selector|Condition|bool) -> None:
super().__init__([condition])
self.condition:Callable[[dict[str, Any|None]], bool]|Selector|Condition|bool = condition
self.ok:bool = False
class If(Action):
def __init__(self:Self,
condition:Condition|Callable[[dict[str, Any|None]], bool]|Selector|bool,
if_actions:Optional[Sequence[Action]] = None,
else_actions:Optional[Sequence[Action]] = None
) -> None:
super().__init__()
self.condition:Condition|Callable[[dict[str, Any|None]], bool]|Selector|bool = condition
self.if_actions:Sequence[Action]|None = if_actions
self.else_actions:Sequence[Action]|None = else_actions
class While(Action):
def __init__(self:Self,
condition:Condition|Callable[[dict[str, Any|None]], bool]|Selector|bool,
actions:Sequence[Action]
) -> None:
super().__init__()
self.condition:Condition|Callable[[dict[str, Any|None]], bool]|Selector|bool = condition
self.actions:Sequence[Action] = actions
class DoWhile(Action):
def __init__(self:Self,
condition:Condition|Callable[[dict[str, Any|None]], bool]|Selector|bool,
actions:Sequence[Action]
) -> None:
super().__init__()
self.condition:Condition|Callable[[dict[str, Any|None]], bool]|Selector|bool = condition
self.actions:Sequence[Action] = actions
class Download(Action):
def __init__(self:Self,
url:str,
key:Optional[str] = None,
attribute:Optional[str] = None,
path:Optional[str] = None,
multiple:bool = False,
overwrite:bool = False
) -> None:
super().__init__()
self.url:str = url
self.key:str|None = key
self.attribute:str|None = attribute
self.path:str|None = path
self.multiple:bool = multiple
self.overwrite:bool = overwrite

View File

@ -0,0 +1,2 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,2 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,2 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self
from AnP.DSLs.ScrapDSL import Action
from AnP.Interfaces.Application import AnPInterface
from AnP.Abstracts.BrowserAbstract import BrowserAbstract
class SeleniumDriver(BrowserAbstract):
def __init__(self:Self, anp:AnPInterface) -> None:
super().__init__(anp)

View File

@ -0,0 +1,309 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self, Any, Optional, Sequence, Callable
from requests import get, Response
from parsel import Selector as ParseSelector, SelectorList as ParseSelectorList
from AnP.DSLs.ScrapDSL import (
Execute, Selector, CSS, XPath, Name, LinkText, Class, ID, Open, Close, Switch,
Go, Get, ForEach, Do, Condition, And, Or, Not, If, While, DoWhile, Download,
Action
)
from AnP.Interfaces.Application import AnPInterface
from AnP.Abstracts.BrowserAbstract import BrowserAbstract
from AnP.Models.XMLScrapTabModel import XMLScrapTabModel
class XMLScrapDriver(BrowserAbstract):
def __init__(self:Self, anp:AnPInterface, key:str, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
super().__init__(anp, key, XMLScrapTabModel, inputs)
def run(self:Self, actions:Sequence[Action], shared:dict[str, Any|None] = {}) -> dict[str, Any|None]:
self.do(Do(*actions), shared)
return shared
def __load(self:Self, url:str) -> Exception|None:
try:
response:Response
with get(url) as response:
self.tabs[self.tab_selected].set(response.text)
except Exception as exception:
return exception
return None
def execute(self:Self, action:Execute, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
try:
action.callback(shared)
action.done = True
except Exception as exception:
action.exception = exception
def open(self:Self, action:Open, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
url:str = shared[action.url] if action.url in shared and isinstance(shared[action.url], str) else action.url
self.tabs[action.key] = XMLScrapTabModel(action.key, url)
self.tab_selected = action.key
if url is not None:
action.exception = self.__load(url)
action.done = action.exception is None
def close(self:Self, action:Close, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
if action.key in self.tabs:
del self.tabs[action.key]
if len(self.tabs):
self.tab_selected = list(self.tabs.keys())[-1]
action.done = True
def switch(self:Self, action:Switch, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
if action.key in self.tabs:
self.tab_selected = action.key
action.done = True
def go(self:Self, action:Go, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
url:str = shared[action.url] if action.url in shared and isinstance(shared[action.url], str) else action.url
action.exception = self.__load(url)
action.done = action.exception is None
def get_selector(self:Self, selector:str, from_item:Optional[Selector] = None, force_multiple:bool = False) -> ParseSelectorList|ParseSelector|None:
if self.tab_selected in self.tabs:
path:ParseSelector = self.tabs[self.tab_selected].selector
items:ParseSelectorList = (
path.css(selector) if isinstance(selector, CSS) else
path.xpath(selector) if isinstance(selector, XPath) else
path.css(f"[name='{selector}']") if isinstance(selector, Name) else
path.css(f"a:contains('{selector}')") if isinstance(selector, LinkText) else
path.css(f".{selector}") if isinstance(selector, Class) else
path.css(f"#{selector}") if isinstance(selector, ID) else
path.css(selector))
return (
items if force_multiple or selector.multiple else
items[0] if len(items) else
None)
return [] if force_multiple or selector.multiple else None
def get(self:Self, action:Get, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
item:ParseSelector|ParseSelectorList|None = last_item if action.selector is None else self.get_selector(action.selector, last_item)
multiple:bool = False if action.selector is None else action.selector.multiple
attribute:str = "::text" if action.attribute == "CDATA" else f"::attr({action.attribute})"
if action.reset and action.key in shared:
del shared[action.key]
if multiple:
if action.key not in shared or not isinstance(shared[action.key], list):
shared[action.key] = []
shared[action.key].extend([subitem.css(attribute).get() for subitem in item])
else:
shared[action.key] = None if item is None else item.css(attribute).get()
def foreach(self:Self, action:ForEach, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
item:ParseSelector|Any|None
done:bool = True
for item in (
self.get_selector(action.items, last_item, True) if isinstance(action.items, Selector) else
action.items):
if not self.anp.working():
done = False
break
if action.callback is not None:
try:
item = action.callback(item)
except Exception as exception:
action.exception = exception
done = False
break
subactions:Do = Do(*action.actions)
self.do(subactions, shared, item if isinstance(item, ParseSelector) else last_item)
if not subactions.done or subactions.exception is not None:
action.exception = subactions.exception
done = False
break
action.done = done
def _and(self:Self, action:And, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:
condition:Callable[[dict[str, Any|None]], bool]|Selector|Condition
action.ok = True
for condition in action.conditions:
if isinstance(condition, Condition):
self.do(condition, shared, last_item)
if not condition.ok:
action.ok = False
break
elif isinstance(condition, Selector):
if not self.get_selector(condition, last_item):
action.ok = False
break
elif callable(condition):
try:
if not condition(shared):
action.ok = False
break
except Exception as exception:
action.exception = exception
action.ok = False
break
def _or(self:Self, action:Or, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:
condition:Callable[[dict[str, Any|None]], bool]|Selector|Condition
action.ok = False
for condition in action.conditions:
if isinstance(condition, Condition):
self.do(condition, shared, last_item)
if condition.ok:
action.ok = True
break
elif isinstance(condition, Selector):
if self.get_selector(condition, last_item):
action.ok = True
break
elif callable(condition):
try:
if condition(shared):
action.ok = True
break
except Exception as exception:
action.exception = exception
action.ok = False
break
def _not(self:Self, action:Not, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:
if isinstance(action.condition, Condition):
self.do(action.condition, shared, last_item)
action.ok = not action.condition.ok
elif isinstance(action.condition, Selector):
action.ok = not self.get_selector(action.condition, last_item)
elif callable(action.condition):
try:
action.ok = not action.condition(shared)
except Exception as exception:
action.exception = exception
action.ok = False
def _if(self:Self, action:If, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
if not isinstance(action.condition, Condition):
action.condition = Condition((action.condition,))
self.do(action.condition, shared, last_item)
if action.condition.done and action.condition.exception is None:
subactions:Do = Do(*((action.if_actions if action.condition.ok else action.else_actions) or []))
self.do(subactions, shared, last_item)
action.done = subactions.done and subactions.exception is None
action.exception = subactions.exception
else:
action.done = action.condition.done
action.exception = action.condition.exception
def _while(self:Self, action:While, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
if not isinstance(action.condition, Condition):
action.condition = Condition((action.condition,))
while True:
if not self.anp.working():
action.done = False
break
self.do(action.condition, shared, last_item)
if not action.condition.done or action.condition.exception is not None:
action.done = action.condition.done
action.exception = action.condition.exception
break
if action.condition.ok:
subactions:Do = Do(*action.actions)
self.do(subactions, shared, last_item)
if not subactions.done or subactions.exception is not None:
action.exception = subactions.exception
action.done = False
break
else:
action.done = True
break
def do_while(self:Self, action:DoWhile, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
if not isinstance(action.condition, Condition):
action.condition = Condition((action.condition,))
while True:
if not self.anp.working():
action.done = False
break
subactions:Do = Do(*action.actions)
self.do(subactions, shared, last_item)
if not subactions.done or subactions.exception is not None:
action.exception = subactions.exception
action.done = False
break
self.do(action.condition, shared, last_item)
if not action.condition.done or action.condition.exception is not None:
action.done = action.condition.done
action.exception = action.condition.exception
break
if not action.condition.ok:
action.done = True
break
def download(self:Self, action:Download, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
if action.key or action.path:
try:
response:Response
url:str = shared[action.url] if action.url in shared and isinstance(shared[action.url], str) else action.url
with get(url) as response:
if response.content is not None:
if action.key:
if action.multiple:
if action.key not in shared or not isinstance(shared[action.key], list):
shared[action.key] = []
shared[action.key].append(response.content)
action.done = True
elif action.overwrite or action.key not in shared:
shared[action.key] = response.content
action.done = True
else:
if action.overwrite or not self.anp.files.exists(action.path):
self.anp.files.save(action.path, response.content)
action.done = True
except Exception as exception:
action.exception = exception

View File

@ -0,0 +1,72 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Optional, Self, Any, Sequence
from abc import ABC, abstractmethod
from AnP.DSLs.ScrapDSL import (
Execute, Action, Open, Close, Switch, Wait, Do, Go, Get, ForEach, And, Or, Not,
If, While, DoWhile, Download
)
from AnP.Abstracts.ModelAbstract import ModelAbstract
class BrowserAbstractInterface(ABC, ModelAbstract):
@abstractmethod
def start(self:Self) -> None:pass
@abstractmethod
def close(self:Self) -> None:pass
@abstractmethod
def is_working(self:Self) -> bool:pass
@abstractmethod
def run(self:Self, actions:Sequence[Action], shared:dict[str, Any|None] = {}) -> dict[str, Any|None]:pass
@abstractmethod
def execute(self:Self, action:Execute, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def open(self:Self, action:Open, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def close(self:Self, action:Close, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def switch(self:Self, action:Switch, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def wait(self:Self, action:Wait, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def do(self:Self, action:Do, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def go(self:Self, action:Go, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def get(self:Self, action:Get, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def foreach(self:Self, action:ForEach, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def _and(self:Self, action:And, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:pass
@abstractmethod
def _or(self:Self, action:Or, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:pass
@abstractmethod
def _not(self:Self, action:Not, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:pass
@abstractmethod
def _if(self:Self, action:If, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def _while(self:Self, action:While, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def do_while(self:Self, action:DoWhile, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
@abstractmethod
def download(self:Self, action:Download, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass

View File

@ -1,10 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from typing import Self, Sequence from typing import Self, Sequence, Any
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from AnP.Abstracts.ModelAbstract import ModelAbstract
class FilesAbstractInterface(ABC): class FilesAbstractInterface(ABC, ModelAbstract):
@abstractmethod @abstractmethod
def get_parents(path:str, levels:int = 1) -> list[str]:pass def get_parents(path:str, levels:int = 1) -> list[str]:pass
@ -43,4 +44,10 @@ class FilesAbstractInterface(ABC):
def save(self:Self, path:str, content:str|bytes) -> bool:pass def save(self:Self, path:str, content:str|bytes) -> bool:pass
@abstractmethod @abstractmethod
def delete(self:Self, path:str) -> bool:pass def delete(self:Self, path:str) -> bool:pass
@abstractmethod
def load_json(self:Self, inputs:Any|None, only_dictionaries:bool = True) -> list[dict[str, Any|None]|Sequence[Any|None]]:pass
@abstractmethod
def list_directory_items(self:Self, path:str) -> list[str]:pass

View File

@ -3,8 +3,9 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Self from typing import Self
from AnP.Abstracts.ModelAbstract import ModelAbstract
class HTTPServersAbstractInterface(ABC): class HTTPServersAbstractInterface(ABC, ModelAbstract):
@abstractmethod @abstractmethod
def start(self:Self) -> None:pass def start(self:Self) -> None:pass

View File

@ -3,8 +3,9 @@
from typing import Any, Self, Optional, Sequence from typing import Any, Self, Optional, Sequence
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from AnP.Abstracts.ModelAbstract import ModelAbstract
class WebSocketServersAbstractInterface(ABC): class WebSocketServersAbstractInterface(ABC, ModelAbstract):
@abstractmethod @abstractmethod
def start(self:Self) -> None:pass def start(self:Self) -> None:pass

View File

@ -20,6 +20,7 @@ from AnP.Interfaces.Managers.WebSocketServersManagerInterface import WebSocketSe
from AnP.Interfaces.Managers.HTTPServersManagerInterface import HTTPServersManagerInterface from AnP.Interfaces.Managers.HTTPServersManagerInterface import HTTPServersManagerInterface
from AnP.Interfaces.Managers.PseudoLoRAsManagerInterface import PseudoLoRAsManagerInterface from AnP.Interfaces.Managers.PseudoLoRAsManagerInterface import PseudoLoRAsManagerInterface
from AnP.Interfaces.Managers.AIInterpretersManagerInterface import AIInterpretersManagerInterface from AnP.Interfaces.Managers.AIInterpretersManagerInterface import AIInterpretersManagerInterface
from AnP.Interfaces.Managers.BrowsersManagerInterface import BrowsersManagerInterface
class AnPInterface(ABC): class AnPInterface(ABC):
@ -41,6 +42,7 @@ class AnPInterface(ABC):
self.http_servers:HTTPServersManagerInterface = None self.http_servers:HTTPServersManagerInterface = None
self.pseudoloras:PseudoLoRAsManagerInterface = None self.pseudoloras:PseudoLoRAsManagerInterface = None
self.ai_interpreters:AIInterpretersManagerInterface = None self.ai_interpreters:AIInterpretersManagerInterface = None
self.browsers:BrowsersManagerInterface = None
@abstractmethod @abstractmethod
def update(self:Self) -> None:pass def update(self:Self) -> None:pass

View File

@ -0,0 +1,27 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self, Any, Sequence
from abc import ABC, abstractmethod
from AnP.Interfaces.Abstracts.BrowserAbstractInterface import BrowserAbstractInterface
from AnP.DSLs.ScrapDSL import Action
class BrowsersManagerInterface(ABC):
@abstractmethod
def update(self:Self) -> None:pass
@abstractmethod
def reset(self:Self) -> None:pass
@abstractmethod
def close(self:Self) -> None:pass
@abstractmethod
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:pass
@abstractmethod
def get(self:Self, key:str) -> BrowserAbstractInterface|None:pass
@abstractmethod
def run(self:Self, key:str, actions:Sequence[Action], shared:dict[str, Any|None] = {}) -> dict[str, Any|None]:pass

View File

@ -0,0 +1,69 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self, Any, Sequence
from AnP.Interfaces.Application.AnPInterface import AnPInterface
from AnP.Abstracts.BrowserAbstract import BrowserAbstract
from AnP.Utils.Checks import Check
from AnP.DSLs.ScrapDSL import Action
class BrowsersManager:
def __init__(self:Self, anp:AnPInterface) -> None:
self.anp:AnPInterface = anp
self.__browsers:dict[str, BrowserAbstract] = {}
self.update()
def update(self:Self) -> None:
key:str
for key in ("default_browsers_files", "browsers_files", "default_browsers", "browsers"):
self.add(self.anp.settings.get(key), True)
def reset(self:Self) -> None:
self.__browsers = {}
self.update()
def close(self:Self) -> None:
for key, browser in list(self.__browsers.items()):
browser.close()
del self.__browsers[key]
def add(self:Self, inputs:Any|None, overwrite:bool = False) -> None:
subinputs:dict[str, Any|None]
for subinputs in self.anp.files.load_json(inputs, True):
key:str
browser:Any|None
for key, browser in subinputs.items():
BrowserClass:type[BrowserAbstract]|None
if Check.is_string(browser):
BrowserClass = self.anp.models.get(BrowserAbstract, browser)
elif issubclass(browser, BrowserAbstract):
BrowserClass = browser
elif Check.is_dictionary(browser) and "type" in browser and Check.is_string(browser["type"]):
BrowserClass = self.anp.models.get(BrowserAbstract, browser["type"])
else:
continue
if BrowserClass and (
overwrite or key not in self.__browsers
):
self.__browsers[key] = BrowserClass(self.anp, key, (
browser if Check.is_dictionary(browser) else
None))
def get(self:Self, key:str) -> BrowserAbstract|None:
return self.__browsers.get(key, None)
def run(self:Self, key:str, actions:Sequence[Action], shared:dict[str, Any|None] = {}) -> dict[str, Any|None]:
return self.__browsers[key].run(actions, shared) if key in self.__browsers else shared

View File

@ -34,6 +34,10 @@ class ControllersManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in self.anp.files.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
key:str
controller:Any|None
for key, controller in subinputs.items(): for key, controller in subinputs.items():
if Check.is_mark_key(key) and controller is None: if Check.is_mark_key(key) and controller is None:
continue continue

View File

@ -33,6 +33,10 @@ class DispatchesManager:
subinputs:dict[str, Any|None] subinputs:dict[str, Any|None]
for subinputs in self.anp.files.load_json(inputs, True): for subinputs in self.anp.files.load_json(inputs, True):
key:str
dispatch:Any|None
for key, dispatch in subinputs.items(): for key, dispatch in subinputs.items():
if Check.is_mark_key(key) and dispatch is None: if Check.is_mark_key(key) and dispatch is None:
continue continue

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self
class CoordenateModel:
def __init__(self:Self,
x:float|int,
y:float|int
) -> None:
self.x:float|int = x
self.y:float|int = y

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self
from random import random
from AnP.Models.CoordenateModel import CoordenateModel
class PositionModel(CoordenateModel):
def __init__(self:Self,
x:float|int,
y:float|int,
size:int|float|list[int|float, int|float]|tuple[int|float, int|float] = 0,
margin:int|float|list[int|float, int|float]|tuple[int|float, int|float] = 0
) -> None:
super().__init__(x, y)
self.size:CoordenateModel = CoordenateModel(*(
size if isinstance(size, (list, tuple)) else
(size, size) if isinstance(size, (int, float)) else
(0, 0)))
self.margin:CoordenateModel = CoordenateModel(*(
margin if isinstance(margin, (list, tuple)) else
(margin, margin) if isinstance(margin, (int, float)) else
(0, 0)))
def get(self:Self) -> tuple[float, float]:
return (
self.x + random() * (self.size.x - 2 * self.margin.x) + self.margin.x,
self.y + random() * (self.size.y - 2 * self.margin.y) + self.margin.y
)

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from typing import Self
from parsel import Selector
class XMLScrapTabModel:
def __init__(self:Self, key:str, url:str) -> None:
self.key:str = key
self.url:str = url
self.data:str = ""
self.selector:Selector|None = None
def set(self:Self, data:str) -> None:
self.data = data
self.selector = Selector(text = data, type = "xml")

View File

@ -10,7 +10,7 @@ from mimetypes import guess_type as get_mime_by_extension
from inspect import FrameInfo, stack as get_stack from inspect import FrameInfo, stack as get_stack
from json import dumps as json_encode, loads as json_decode from json import dumps as json_encode, loads as json_decode
from base64 import b64encode as base64_encode, b64decode as base64_decode from base64 import b64encode as base64_encode, b64decode as base64_decode
from random import choice as random_choice from random import choice as random_choice, random as random_number
from AnP.Utils.Checks import Check from AnP.Utils.Checks import Check
from AnP.Utils.Patterns import RE from AnP.Utils.Patterns import RE
@ -350,4 +350,12 @@ class Common:
def random(items:str|Sequence[Any|None]) -> Any|None: def random(items:str|Sequence[Any|None]) -> Any|None:
return random_choice(items) if ( return random_choice(items) if (
Check.is_string(items) or Check.is_array(items) Check.is_string(items) or Check.is_array(items)
) and len(items) else None ) and len(items) else None
@staticmethod
def random_number(minimum:Optional[int|float] = None, maximum:Optional[int|float] = None) -> int|float:
if minimum is None:
return random_number()
if maximum is None:
return random_number() * minimum
return random_number() * (maximum - minimum) + minimum

View File

@ -8,6 +8,7 @@ from AnP.Drivers.HTTPSocketServerDriver import HTTPSocketServerDriver
from AnP.Drivers.HTTPServerDriver import HTTPServerDriver from AnP.Drivers.HTTPServerDriver import HTTPServerDriver
from AnP.Drivers.OllamaDriver import OllamaDriver from AnP.Drivers.OllamaDriver import OllamaDriver
from AnP.Drivers.FilesDriver import FilesDriver from AnP.Drivers.FilesDriver import FilesDriver
from AnP.Drivers.XMLScrapDriver import XMLScrapDriver
from AnP.Controllers.CloneController import CloneController from AnP.Controllers.CloneController import CloneController
from AnP.Controllers.AITranslateController import AITranslateController from AnP.Controllers.AITranslateController import AITranslateController
@ -20,7 +21,8 @@ inputs:dict[str, dict[str, Any|None]] = {
"HTTPSocketServerDriver" : HTTPSocketServerDriver, "HTTPSocketServerDriver" : HTTPSocketServerDriver,
"HTTPServerDriver" : HTTPServerDriver, "HTTPServerDriver" : HTTPServerDriver,
"OllamaDriver" : OllamaDriver, "OllamaDriver" : OllamaDriver,
"FilesDriver" : FilesDriver "FilesDriver" : FilesDriver,
"XMLScrapDriver" : XMLScrapDriver
} }
} }