Translate.hex

mIRC Scripting Guest 5 Views Size: 6.56 KB Posted on: Jul 12, 26 @ 6:49 PM
  1. ; Auto translations for HexDroid
  2. ;
  3. ; auto-translate incoming foreign-language lines.
  4. ; Usage: /tr something
  5. ; ENDPOINT: point %ep at any LibreTranslate server. A bare base URL is fine, the script
  6. ; appends the "/translate" API path for you.
  7. ;
  8. ; The public https://libretranslate.com endpoint REQUIRES an API key; a self-hosted
  9. ; instance usually does not. Edit via Scripts > translate.hex > Edit.
  10.  
  11. on LOAD {
  12.   set %lang $setting(applang)
  13.   if ($len(%lang) == 0) { set %lang en }
  14.   ; ===== EDIT THESE =================================================
  15.   set %ep https://translate.boxlabs.uk; your LibreTranslate server (base URL is fine)
  16.   set %apikey                          ; API key (blank = none / self-hosted)
  17.   ; =================================================================
  18.   set %trwarned 0                      ; so a failing endpoint warns once, not per line
  19.   set %maxlen 500                      ; don't ship very long lines to the endpoint
  20.   set %minconf 50                      ; ignore translations the detector isn't sure about (0-100)
  21.   set %recent $list()                  ; recently-seen lines, to skip bouncer/CHATHISTORY repeats
  22.   ; normalise %ep to the POST /translate path: drop a trailing slash, then add /translate
  23.   ; unless it's already there (matching the path, not the "translate" that may be in a host).
  24.   if ($right(%ep, 1) == /) { set %ep $left(%ep, $calc($len(%ep) - 1)) }
  25.   if ($right(%ep, 10) != /translate) { set %ep %ep/translate }
  26. }
  27.  
  28. ; Build the LibreTranslate request body for text $1-, honouring an optional API key. The
  29. ; values are url-encoded, so the result is a single space-free token, which is what
  30. ; http.post needs since it splits its arguments on spaces.
  31. alias tr_body {
  32.   if ($len(%apikey) > 0) { return q=$urlencode($1-)&source=auto&target=%lang&api_key=%apikey }
  33.   return q=$urlencode($1-)&source=auto&target=%lang
  34. }
  35.  
  36. ; Turn an HTTP status into a human hint ($1 = status).
  37. alias tr_diag {
  38.   if ($1 == 405) { return status 405 (Method Not Allowed) from %ep. }
  39.   if ($1 == 403) { return status 403: the endpoint needs an API key, set %apikey (or the key is wrong). }
  40.   if ($1 == 400) { return status 400: the server rejected the request (often a missing API key or unsupported language). }
  41.   if ($1 == 0)   { return could not reach %ep, check the URL/TLS and that the server is up. }
  42.   return status $1
  43. }
  44.  
  45. ; Auto-translate every incoming line that isn't ours and looks like it might be foreign.
  46. ; The buffer, sender nick, and original text are threaded through as context
  47. ; ($1 = buffer, $2 = nick, $3- = original) so the callback can place the result, attribute
  48. ; it, show the language pair, skip our own language, and reject same-language garble.
  49. on TEXT {
  50.   ; --- never translate our own lines. $isme is the primary signal, but a bouncer replaying
  51.   ; our messages (CHATHISTORY / echo-message) can arrive without it, so also match our nick.
  52.   if ($isme == true) { return }
  53.   if ($nick == $me) { return }
  54.   ; --- cheap skips: blanks, URLs, over-long lines
  55.   if ($len($text) < 2) { return }
  56.   if ($left($text, 4) == http) { return }
  57.   if ($len($text) > %maxlen) { return }
  58.   ; --- skip ALL-CAPS Latin acronyms / shouting (LOL, BRB, AFK): uppercase-Latin/digits/spaces
  59.   ; only. This never matches lowercase Latin or non-Latin scripts, so real foreign text is kept.
  60.   if ($re_match($text, ^[A-Z0-9 ]+$) == true) { return }
  61.   ; --- skip lines that are mostly not letters (timestamps, scores, "+50 -3 (id 42)"): strip
  62.   ; everything that isn't a letter (\p{L} keeps Cyrillic/CJK/accented too) and require the
  63.   ; remaining letters to be at least half the length, so number/symbol noise never gets sent.
  64.   set %letters $re_replace($text, [^\p{L}], )
  65.   if ($calc($len(%letters) * 2) < $len($text)) { return }
  66.   ; --- de-duplicate: a bouncer often delivers the same line live AND from history. Skip a line
  67.   ; we translated recently (keyed by network+buffer so identical text on two nets still works).
  68.   ; Note: '|' is the command separator, so the key uses '~' as its delimiter.
  69.   set %key $network~$buffer~$text
  70.   if ($has(%recent, %key) == true) { return }
  71.   push %recent %key
  72.   if ($len(%recent) > 24) { set %recent $slice(%recent, 1, $len(%recent)) }
  73.   ; source=auto makes LibreTranslate report the detected language so we can skip our own
  74.   http.post %ep $tr_body($text) tr_auto $buffer $nick $text
  75. }
  76.  
  77. on SIGNAL:tr_auto {
  78.   if ($httpok == true) {
  79.     set %src $json($httpbody, detectedLanguage.language)
  80.     set %conf $json($httpbody, detectedLanguage.confidence)
  81.     set %out $json($httpbody, translatedText)
  82.     set %orig $3-
  83.     ; if the server omitted a confidence, don't let that filter everything out
  84.     if ($len(%conf) == 0) { set %conf 100 }
  85.     if ($len(%src) == 0) { set %src ? }
  86.     if ($len(%out) == 0) { return }
  87.     ; detected source must differ from our language (skip English/acronyms read as English)
  88.     if (%src == %lang) { return }
  89.     ; detector must be reasonably sure
  90.     if (%conf < %minconf) { return }
  91.     ; output must actually differ from the original (a same-language round-trip is identical)
  92.     if (%out == %orig) { return }
  93.     ; --- word-overlap guard: a real translation replaces most words, so if half or more of the
  94.     ; translation's words were already in the original it is almost certainly a same-language
  95.     ; garble (LibreTranslate mis-detecting informal English, eg "peanutbutter banana sandwich"
  96.     ; -> "banana banana butter sandwich"). Drop those; genuine foreign text shares ~no words.
  97.     set %tw $split($lower(%out))
  98.     set %ow $split($lower(%orig))
  99.     set %tn $len(%tw)
  100.     set %hit 0
  101.     set %i 0
  102.     while (%i < %tn) {
  103.       set %w $get(%tw, %i)
  104.       if ($has(%ow, %w) == true) { inc %hit }
  105.       inc %i
  106.     }
  107.     if ($calc(%hit * 2) >= %tn) { return }
  108.     ; passed every guard - show it, attributed to the sender with the src->dst language pair
  109.     echo $1$2 (%src→%lang): %out
  110.   }
  111.   else {
  112.     ; surface a failure once, with a status-specific hint so the real cause is obvious
  113.     if (%trwarned == 0) {
  114.       set %trwarned 1
  115.       echo $1 *** translate: request failed. $tr_diag($httpstatus)
  116.     }
  117.   }
  118. }
  119.  
  120. ; manual: /tr <text>  (always translates, ignoring the auto-skips)
  121. alias tr {
  122.   if ($len($1-) == 0) { echo $chan usage: /tr <text> | return }
  123.   http.post %ep $tr_body($1-) tr_show $chan
  124. }
  125. on SIGNAL:tr_show {
  126.   if ($httpok == true) {
  127.     set %src $json($httpbody, detectedLanguage.language)
  128.     if ($len(%src) == 0) { set %src ? }
  129.     echo $1(%src→%lang): $json($httpbody, translatedText)
  130.   }
  131.   else { echo $1 *** translate failed. $tr_diag($httpstatus) }
  132. }

Raw Paste

Comments 0
Login to post a comment.
  • No comments yet. Be the first.
Login to post a comment. Login or Register
We use cookies. To comply with GDPR in the EU and the UK we have to show you these.

We use cookies and similar technologies to keep this website functional (including spam protection via Google reCAPTCHA or Cloudflare Turnstile), and — with your consent — to measure usage and show ads. See Privacy.