#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_WebSocketsServersManager_start" : null,
"default_web_sockets_servers" : {
"anp" : {
"type" : "WebSocketServerDriver",
"host" : "",
"port" : 18765
}
},
"AnP_WebSocketsServersManager_end" : null,
"AnP_HTTPServersManager_start" : null,
"default_http_servers" : {
"anp" : {
"type" : "HTTPServerDriver",
"type2" : "HTTPSocketServerDriver",
"host" : "",
"port" : 18000
}
},
"AnP_HTTPServersManager_end" : null,
"AnP_PseudoLoRAsManager_start" : null,
@ -225,34 +210,6 @@
"AnP_PseudoLoRAsManager_end" : 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_TitlesManager_start" : null,

View File

@ -61,7 +61,15 @@
"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_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;
};
/**
* @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]
* @returns {Array.<any|null>}
@ -64,20 +134,49 @@ export const AIChatComponent = (function(){
this.build = (inputs = null) => {
/** @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({
class : "aichat",
data_name : Common.get_value("name", inputs, "aichat")
data_name : name,
data_session : session
}, [
anp.components.i18n(name, "legend"),
Section({class : "conversations"}, [
Article({
class : "messages",
data_conversation : anp.unique_keys.get(),
data_selected : true
})
]),
Section({class : "conversations"}, conversations.map(conversation => Article({
class : "messages",
data_conversation : conversation,
data_selected : true
}))),
Form({
method : "post",
action : "#",
@ -172,52 +271,6 @@ export const AIChatComponent = (function(){
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
@ -238,8 +291,6 @@ export const AIChatComponent = (function(){
if(text){
console.log([item, item.querySelector("[name=message]"), item.querySelector("[name=message]").parentNode.parentNode.getAttribute("data-connection")]);
/** @type {string} */
const data_id = anp.unique_keys.get(),
/** @type {[string, string, string]} */

View File

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

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){
/** @type {SessionModel} */
const self = this;
const self = this,
/** @type {Object.<string, any|null>} */
data = {};
/** @type {number|null} */
let id = null,
/** @type {Array.<string>} */
permissions = [],
/** @type {number} */
last_check = 0;
last_check = 0,
/** @type {string|null} */
cookie_key = null,
/** @type {integer|null} */
timeout = null;
/** @type {string|null} */
this.nick = null;
@ -43,11 +49,36 @@ export const SessionModel = (function(){
* @access private
*/
const constructor = () => {
id = Common.get_value("id", inputs, null);
permissions = Common.get_value("permissions", inputs, []);
self.nick = Common.get_value("nick", inputs, null);
self.type = Common.get_value("type", inputs, self.type);
/** @type {string|null} */
const cookie = anp.cookies.get(cookie_key = Common.get_value("cookie", inputs, null));
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();
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);
/**
* @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();
};

View File

@ -32,6 +32,7 @@
"raw_html" : "Código HTML",
"minimum" : "Mínimo",
"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.PseudoLoRAsManager import PseudoLoRAsManager
from AnP.Managers.AIInterpretersManager import AIInterpretersManager
from AnP.Managers.BrowsersManager import BrowsersManager
from AnP.Utils.Common import Common
from AnP.Utils.Patterns import RE
@ -62,6 +63,7 @@ class AnP:
self.http_servers:HTTPServersManager = HTTPServersManager(self)
self.pseudoloras:PseudoLoRAsManager = PseudoLoRAsManager(self)
self.ai_interpreters:AIInterpretersManager = AIInterpretersManager(self)
self.browsers:BrowsersManager = BrowsersManager(self)
def update(self:Self) -> None:
self.settings.update()
@ -81,6 +83,7 @@ class AnP:
self.http_servers.update()
self.pseudoloras.update()
self.ai_interpreters.update()
self.browsers.update()
def reset(self:Self) -> None:
self.settings.reset()
@ -98,6 +101,7 @@ class AnP:
self.web_socket_servers.reset()
self.http_servers.reset()
self.ai_interpreters.reset()
self.browsers.reset()
def close(self:Self) -> None:
self.__working = False
@ -105,6 +109,7 @@ class AnP:
self.ai_interpreters.close()
self.web_socket_servers.close()
self.http_servers.close()
self.browsers.close()
def working(self:Self) -> bool:
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
# -*- coding: utf-8 -*-
from typing import Self, Sequence
from typing import Self, Sequence, Any
from abc import ABC, abstractmethod
from AnP.Abstracts.ModelAbstract import ModelAbstract
class FilesAbstractInterface(ABC):
class FilesAbstractInterface(ABC, ModelAbstract):
@abstractmethod
def get_parents(path:str, levels:int = 1) -> list[str]:pass
@ -44,3 +45,9 @@ class FilesAbstractInterface(ABC):
@abstractmethod
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 typing import Self
from AnP.Abstracts.ModelAbstract import ModelAbstract
class HTTPServersAbstractInterface(ABC):
class HTTPServersAbstractInterface(ABC, ModelAbstract):
@abstractmethod
def start(self:Self) -> None:pass

View File

@ -3,8 +3,9 @@
from typing import Any, Self, Optional, Sequence
from abc import ABC, abstractmethod
from AnP.Abstracts.ModelAbstract import ModelAbstract
class WebSocketServersAbstractInterface(ABC):
class WebSocketServersAbstractInterface(ABC, ModelAbstract):
@abstractmethod
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.PseudoLoRAsManagerInterface import PseudoLoRAsManagerInterface
from AnP.Interfaces.Managers.AIInterpretersManagerInterface import AIInterpretersManagerInterface
from AnP.Interfaces.Managers.BrowsersManagerInterface import BrowsersManagerInterface
class AnPInterface(ABC):
@ -41,6 +42,7 @@ class AnPInterface(ABC):
self.http_servers:HTTPServersManagerInterface = None
self.pseudoloras:PseudoLoRAsManagerInterface = None
self.ai_interpreters:AIInterpretersManagerInterface = None
self.browsers:BrowsersManagerInterface = None
@abstractmethod
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]
for subinputs in self.anp.files.load_json(inputs, True):
key:str
controller:Any|None
for key, controller in subinputs.items():
if Check.is_mark_key(key) and controller is None:
continue

View File

@ -33,6 +33,10 @@ class DispatchesManager:
subinputs:dict[str, Any|None]
for subinputs in self.anp.files.load_json(inputs, True):
key:str
dispatch:Any|None
for key, dispatch in subinputs.items():
if Check.is_mark_key(key) and dispatch is None:
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 json import dumps as json_encode, loads as json_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.Patterns import RE
@ -351,3 +351,11 @@ class Common:
return random_choice(items) if (
Check.is_string(items) or Check.is_array(items)
) 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.OllamaDriver import OllamaDriver
from AnP.Drivers.FilesDriver import FilesDriver
from AnP.Drivers.XMLScrapDriver import XMLScrapDriver
from AnP.Controllers.CloneController import CloneController
from AnP.Controllers.AITranslateController import AITranslateController
@ -20,7 +21,8 @@ inputs:dict[str, dict[str, Any|None]] = {
"HTTPSocketServerDriver" : HTTPSocketServerDriver,
"HTTPServerDriver" : HTTPServerDriver,
"OllamaDriver" : OllamaDriver,
"FilesDriver" : FilesDriver
"FilesDriver" : FilesDriver,
"XMLScrapDriver" : XMLScrapDriver
}
}