Ich arbeite zur Zeit an einem msgDialog modul mit dem sich ein chatBot erstellen lässt. Dabei habe ich jedoch ein Problem mit umlauten. Wenn diese in meiner Nachricht angegeben werden bekomme ich nach einiger Zeit folgende Fehlermeldung im Log:
2017.09.27 10:10:02 3: msg MichaelP: ID=1234567890.24533.1 TYPE=push ROUTE=myTelegramBot STATUS=OK PRIORITY=0 TITLE='' MSG='Ich kann folgendes für dich tun'
2017.09.27 10:10:10 3: TelegramBot_Callback myTelegramBot: resulted in NonBlockingGet timed out on read from <hidden> after 30s from SendIt
2017.09.27 10:10:10 3: TelegramBot_Callback myTelegramBot: Reached max retries (ret: NonBlockingGet timed out on read from <hidden> after 30s) for msg 50673063 : Ich kann folgendes für dich tun
Der Dialog ist als json aufgebaut:
{ "inceptum": {
"match": "\/?(start|inceptum)",
"message": [
"Ich kann folgendes für dich tun:"
]
},
"abbrechen": {
"commands":"deletereading TYPE=msgDialog %recipient%_history",
"message": [
"Dialog abgebrochen.",
"",
"/inceptum"
]
},
"beenden": {
"commands": "deletereading TYPE=msgDialog %recipient%_history",
"message": [
"Dialog beendet.",
"",
"/inceptum"
]
}
}
Auswerten tue ich das mit
my $dialog = encode('UTF-8', $hash->{DEF});
$dialog = eval{decode_json($dialog)};
Wenn ich keine Umlaute angebe funktioniert es bisher wie gewollt. Kann mir jemand auf die Sprünge helfen wie ich das beheben kann?
Grüße,
igami
Eigentlich sollte alles intern in FHEM in UTF-8 sein, auch alle gespeicherten Dateien und die Kommunikation mit der Aussenwelt (telnet/FHEMWEB) sollte diese Kodierung verwenden. Auf dem ersten Blick und ohne den Rest zu kennen sollte encode nicht notwendig sein.
Wenn ich das encode weg lasse bekomme ich statt dem Umlaut im Log ein �.
Eingegeben wird alles über das DEF Feld in der Detailansicht.
Im Modul verwende ich
fhem("msg push \@$device $message");
Wenn ich eine Nachricht direkt in der Kommandozeile veranlasse kommt diese auch wie gewollt an:
msg push @MichaelP Ich kann folgendes für dich tun
oder
{fhem("msg push \@MichaelP Ich kann folgendes für dich tun")};
Momentan bin ich etwas ratlos warum es nicht funktioniert.
Ich habe endlose Stunden in die verquere Art investiert, in der perl mit unicode-Strings umgeht und kann das leider auch nicht komplett aufklären, aber folgende Informationen zu dem was passiert:
Wenn der entsprechende Fehler (resulted in NonBlockingGet timed out on read) bei telegram zurückkommt ist folgendes Problem aufgetreten:
1) Es handelt sich um einen String mit Zeichen, die nicht in einem Byte dargestellt werden können (utf-8)
2) perl markiert die Stringvariable intern als unicode-String
3) eine length operation auf dem String liefert nicht die Anzahl Bytes zurück sondern die Anzahl Zeichen (also weniger als Bytes)
4) in httputils wird beim Versenden die Content-length mit dieser inkorrekten Anzahl Bytes gesendet
5) Es kommt keine Antwort von telegram zurück
Leider unterscheiden sich auch perl-Versionen hier in der Behandlung und es gab Schwierigkeiten, die bei manchen Installationen auftraten und bei anderen nicht, ohne, dass ich die Details rausgefunden habe.
Einen Versuch wert kann es sein einen String per utf8::downgrade( zu behandeln
Siehe da, mit
attr myTelegramBot utf8Special 1
funktioniert es.
Vielen Dank für die, soweit wie mögliche, Aufklärung.
Ja das wollte ich noch erwähnen, ich habe utf8Special genau für diese "unerklärlichen" Fälle hinzugefügt
Schön, dass das klappt
Ich habe nun mal
$dialog = eval{decode_json($dialog)};
durch
[/code]
$dialog = eval{JSON->new->decode($dialog)};
[/code]
ersetzt.
"decode_json" ist eigentlich die Abkürzung für "JSON->new->utf8->decode". Ich habe also bewusst auf das UTF8 verzichtet, da es hierbei zu Probelemen kommt.
Das führt leider immer noch nicht dazu, dass ich auf das utf8Special verzichten kann, aber an der Stelle
Debug("message: $message");
$dialog = eval{JSON->new->encode($dialog)};
$dialog =~ s/\$message/$message/g;
Debug("dialog: $dialog");
$dialog = eval{JSON->new->decode($dialog)};
hat es sonst dazu geführt, dass aus "Spülmittel" "Sp�lmittel" wurde.
Versuch doch mal folgendes (vor decode-json):
utf8::downgrade($dialog) if ( utf8::is_utf8($dialog) );
Zitat von: viegener am 29 September 2017, 12:24:39
Versuch doch mal folgendes (vor decode-json):
utf8::downgrade($dialog) if ( utf8::is_utf8($dialog) );
Ich habe erstmal
Debug("message is utf8") if(utf8::is_utf8($message));
Debug("dialog is utf8") if(utf8::is_utf8($dialog));
eingebaut und stelle fest, ist kein utf8. Bleibt nur die Frage was es dann ist und wie bekomme ich heraus, was es ist?
Modul im Anhang.
Wenn aber utf8special in telegram hilft, dann wird doch irgendwo utf8 daraus (denn das schlägt auch nur zu, wenn is_utf8 gilt). Also wird irgendwo später aus message doch noch utf8
Habe gerade in telegramBot nochmal geschaut, es sieht aber nicht so aus, als ob da etwas beteiligt wäre, was unabsichtlich is_utf8 setzen könnte.
Kann das flag nachher noch hinzukommen?
Hast Du ein use utf8 in Deinem Modul?
Zitat von: viegener am 29 September 2017, 13:13:30
Wenn aber utf8special in telegram hilft, dann wird doch irgendwo utf8 daraus (denn das schlägt auch nur zu, wenn is_utf8 gilt). Also wird irgendwo später aus message doch noch utf8
Habe gerade in telegramBot nochmal geschaut, es sieht aber nicht so aus, als ob da etwas beteiligt wäre, was unabsichtlich is_utf8 setzen könnte.
Kann das flag nachher noch hinzukommen?
Die Nachricht wird über den msg Befehl abgesetzt und in dem Modul gibt es die Zeile 272
eval '$o = decode_json( Encode::encode_utf8($1) ); 1';
In meinem Modul mache ich kein utf8 draus.
Zitat von: viegener am 29 September 2017, 13:13:30
Hast Du ein use utf8 in Deinem Modul?
Ja, ist drin.
leider not so easy ...
utf8 kann ein bytestream (1-x mal 8 bit pro Zeichen) sein oder ein charstream (1 Zeichen darf > 8 bit sein).
Perl unterscheidet das nicht (so einfach).
is_utf8 gibt nur ein internes Flag zurück - unabhängig vom tatsächlichen Inhalt der Struktur.
Das, und verschiedene JSON und perl Versionen, ergibt zusammen den murks.
@viegener: dem Sch.. hab ich ebenfalls _einige_ dunkle Stunden ;) zu verdanken.
Zu dem Zeitpunkt wo ich Prüfe ist es ja noch gar kein JSON, sonder nur der Inhalt von DEF, bzw. der Teil eines Events:
sub msgDialog_Notify($$) {
my ($hash, $dev_hash) = @_;
my $SELF = $hash->{NAME};
my $TYPE = $hash->{TYPE};
my $device = $dev_hash->{NAME};
return if(IsDisabled($SELF));
my @events = @{deviceEvents($dev_hash, 1)};
return unless(
@events
&& AttrVal($SELF, "allowed", "") =~ m/(^|,)($device|everyone)(,|$)/
);
foreach my $event (@events){
next unless($event =~ m/fhemMsgPushReceived: (.+)/);
msgDialog_progress($hash, $device, $1);
}
return;
}
sub msgDialog_progress($$$;$) {
sub msgDialog_progress($$$;$) {
my ($hash, $recipients, $message, $force) = @_;
Debug("message is utf8") if(utf8::is_utf8($message));
my $SELF = $hash->{NAME};
my $TYPE = $hash->{TYPE};
$recipients =
$recipients eq "everyone" ?
join(",", devspec2array(
"TYPE=(ROOMMATE|GUEST):FILTER="
. "msgContactPush=.+:FILTER=msgRecipientPush=.+"
))
: $recipients;
my @oldHistory;
@oldHistory = split("\\|", ReadingsVal($SELF, $recipients."_history", ""))
unless($force);
push(@oldHistory, split("\\|", $message));
my (@history);
my $dialog =$hash->{DEF};
Debug("dialog is utf8") if(utf8::is_utf8($dialog));
...
"utf8::valid" ist übrigens der Meinung, dass es utf8 ist ???
Zitat von: herrmannj am 29 September 2017, 13:17:44
leider not so easy ...
utf8 kann ein bytestream (1-x mal 8 bit pro Zeichen) sein oder ein charstream (1 Zeichen darf > 8 bit sein).
Perl unterscheidet das nicht (so einfach).
is_utf8 gibt nur ein internes Flag zurück - unabhängig vom tatsächlichen Inhalt der Struktur.
Das, und verschiedene JSON und perl Versionen, ergibt zusammen den murks.
@viegener: dem Sch.. hab ich ebenfalls _einige_ dunkle Stunden ;) zu verdanken.
Genau diese Unterscheidung meine ich mit "markiert den String als unicode" -> downgrade setzt wohl intern im wesentlichen dieses Flag zurück (meine Vermutung - trial and error) - warum das auf manchen Installationen nötig ist und bei anderen nicht verstehe ich leider immer noch nicht genau, teile aber genau Deine Vermutung
Und absolut: Murks und überhaupt nicht easy :(
Zitat von: igami am 29 September 2017, 13:17:34
Die Nachricht wird über den msg Befehl abgesetzt und in dem Modul gibt es die Zeile 272
eval '$o = decode_json( Encode::encode_utf8($1) ); 1';
In meinem Modul mache ich kein utf8 draus.
Ja, ist drin.
Dazu kann ich 2 Dinge ergänzen:
1. Ich hasse den Sch*** genauso 8)
2. Die zitierte Codezeile kommt eigentlich nicht mehr zum Einsatz, wenn man die neue Syntax des msg Befehls verwendet, die auf parseParams aufbaut. Das heißt momentan wird sich in dem msg Befehl gar nicht um Codierung gekümmert - wie es rein kommt so geht es an den set-Befehl des jeweiligen Moduls wieder raus (theoretisch).