Страницы

Поиск по вопросам

вторник, 30 апреля 2019 г.

Telegram bot php. Inline keyboard. Подскажите, что делать дальше с callback_data. Что бы после нажатия на кнопку inline keyboard - бот ее обработал

Подскажите, пожалуйста как написать простой сценарий для бота, например: написал команду "/test", бот спрашивает: "Вы уверены?" пишешь в ответ: "Да" бот отправляет следующее сообщение по сценарию, если пишешь "нет" - сценарий заканчивается. Важно, что бы мой ответ "Да" не обрабатывался ботом, пока он не спросит "Вы уверены?"(после команды /test)
$access_token = '...'; $api = 'https://api.telegram.org/bot' . $access_token;
$output = json_decode(file_get_contents('php://input'), TRUE); $chat_id = $output['message']['chat']['id']; $first_name = $output['message']['chat']['first_name']; $message = $output['message']['text']; switch($message) { case '/test': sendMessage($chat_id, "Вы уверены?"); break; default: $sorry_text = $first_name . ' , мне нечего ответить'; sendMessage($chat_id, $sorry_text); } function sendMessage($chat_id, $message, $encodedMarkup) { file_get_contents($GLOBALS['api'] . '/sendMessage?chat_id=' . $chat_id . '&text=' . urlencode($message) . $encodedMarkup); }
Понял, что это делается через ForceReply, но как пока не разобрался. UPDATE сделал inline keybord, но пока не понял как выводить сообщение в зависимости от нажатой кнопки case '/test':
$x1 = array("text"=>"First Button","callback_data"=>"test1"); $x2 = array("text"=>"Second Button","callback_data"=>"test2"); $opz = [[$x1,$x2]]; $keyboard=array("inline_keyboard"=>$opz); $keyboard = json_encode($keyboard); sendMessage($chat_id, "testt2", $keyboard); break;
т.е. сейчас при нажатии на кнопку ничего не вывыодится


Ответ

Вот рабочий код, если кому нужен.
"Google url","url"=>"http://google.com"); $inline_button2 = array("text"=>"work plz","callback_data"=>'/plz'); $inline_keyboard = [[$inline_button1,$inline_button2]]; $keyboard=array("inline_keyboard"=>$inline_keyboard); $replyMarkup = json_encode($keyboard); sendMessage($chat_id, "ok", $replyMarkup); break; } switch($data){ case '/plz': sendMessage($chat_id_in, "plz"); break; } function sendMessage($chat_id, $message, $replyMarkup) { file_get_contents($GLOBALS['api'] . '/sendMessage?chat_id=' . $chat_id . '&text=' . urlencode($message) . '&reply_markup=' . $replyMarkup); }

Многопоточность в Django

В интеренете куче разрозненной информации многопоточности в Django. Некоторые говорят, что она держится на костылях, некоторые утверждают, что ее вообще нет... Есть ли в Django стандартные механизмы для многопоточности и как вообще с этим обстоят дела ?


Ответ

Как сказали выше, сервер запускает джангу в нужное количество процессов. Сама джанга написана в синхронном стиле, и по словам разработчиков на асинхронный лад её переписать невозможно.
А многопоточность средствами джанги не предусмотрена. Тем не менее никто не мешает открывать нужные потоки и процессы средствами самого python.

Как правильно реализовать callback в java?

Я недавно начал для себя изучать Android и собственно java, до этого всегда писал на nodeJS, и за долгое время очень привык к js.
И вот, в процессе изучения, настал момент, когда я решил написать один единственный класс который будет отправлять запросы, и в результате ошибки или успеха будет выполнять соответствующие методы класса из которого мы и шлем этот запрос.
Есть класс MainActivity в котором по клику на кнопку шлем запрос на сервер и ответ вставляем в TextView. Сейчас я реализовал коллбэки таким образом:
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
protected void onCreate(Bundle savedInstanceState) {
sendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DoRequestClass doRequestClass = new DoRequestClass(){
@Override public void onOK(JSONObject response) { successReq(response); }
@Override public void onError(VolleyError error) { errorReq(error); } }; doRequestClass.execute("http://myserver.local", MainActivity.this); } });
public void successReq(JSONObject response){ txtDisplay.setText("Response => "+response.toString()); }
public void errorReq(VolleyError error){ txtDisplay.setText("Error => "+error.toString()); }
// other methods
Такой вот для теста абстрактный класс DoRequestClass
public abstract class DoRequestClass implements DoRequestInterface {
@Override public void execute(String url, Context context) { RequestQueue queue = Volley.newRequestQueue(context);
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener() {
@Override public void onResponse(JSONObject response) { DoRequestClass.this.onOK(response); }
}, new Response.ErrorListener() {
@Override public void onErrorResponse(VolleyError error) { DoRequestClass.this.onError(error); }
});
queue.add(jsObjRequest); } }
С интерфейсом DoRequestInterface
interface DoRequestInterface { void execute(String url, Context context);
void onOK(JSONObject response);
void onError(VolleyError error); }
Все описанное выше работает, но можно ли как-то передать successReq и errorReq в DoRequestClass таким образом, как бы мы это делали это на javascript, о то что я имею ввиду, о желанном результате, смотрите как бы я хотел примерно чтоб они передавались:
sendButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DoRequestClass doRequestClass = new DoRequestClass(); doRequestClass.setSuccessCallback(successReq); doRequestClass.setErrorCallback(errorReq); doRequestClass.execute("http://lessons.flexiblejs.ru/test.php", MainActivity.this); } });
Как вы поняли из кода выше, setSuccessCallback и setErrorCallback это методы которые будут говорить классу DoRequestClass какие методы класса MainActivity нужно выполнить после успешного или не успешного запроса.
Такое или подобное прекрасно работало бы в javascript'e, но такой java-код работать не будет. Вопрос в том, как создать что-то такое или приближенное к этому? И возможно ли вообще? Суть в том чтобы не писать лишний код и создавать лишнее методы типа как в моем рабочем примере onOK и onError
Прошу вас по возможности дать как можно более развернутый ответ с пояснениями, т.к. мои познания в java совсем не велики.


Ответ

Рефакторинг:
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener, Response.Listener, Response.ErrorListener {
protected void onCreate(Bundle savedInstanceState) { ((Button)findViewById(R.id.send)).setOnClickListener(this); }
@Override public void onClick(View v){ switch(v.getId()){ case R.id.send: doRequestClass.execute("http://myserver.local", this, this, this); break; } }
@Override public void onResponse(JSONObject response) { txtDisplay.setText("Response => "+response.toString()); }
@Override public void onErrorResponse(VolleyError error) { txtDisplay.setText("Error => "+error.toString()); } }
DoRequestClass.java
public class DoRequestClass {
@Override public void execute(String url, Context context, Response.Listener listener, Response.ErrorListener listenerError) { RequestQueue queue = Volley.newRequestQueue(context); JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, listener, listenerError); queue.add(jsObjRequest); } }
На самом деле вы продублировали интерфейсы Response.Listener и Response.ErrorListener, просто раскрыв generic и объединив их в один. В приведенном примере это просто не нужно. Впрочем вы можете реализовать как у вас и было этот интерфейс, например обработать response и прокинуть его дальше:
JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener(){ @Override public void onResponse(JSONObject response) { Log.d(TAG, "JSON:"+ response.toString()); listener.onResponse(response);// listener будет final } },listenerError);
Для колбэков используется свой интерфейс (или несколько) и вносить в него метод execute смысла не очень много. В частности execute может быть в одном классе, а колбэк - в другом.
ЗЫ В приведенном коде есть один недочет. Вы каждый раз создаете новую очередь запросов. Так делать не стоит, для всего приложения достаточно одной очереди. Вариантов когда нужно несколько очередей, притом фиксированное количество не так много, например когда реализуется работа с несколькими акаунтами, на разные сервера слать запросы в отдельных очередях... Поэтому в классе приложения (например) стоит создать синглтон для очереди и брать ее оттуда. Либо сделать DoRequestClass (назвав его RequestManager'ом, например) синглтоном с инициализированной один раз очередью...
ЗЗЫ Еще перенес обработчик клика в активити, это по желанию "на вкус и цвет".

Доступ к приватным полям класса предка

Насколько я понимаю, при создании объекта класса через new выделяется область в Heap для хранения всех полей как самого класса, так и нестатичных публичных (и protected) полей всех его предков. А затем поочерёдно вызываются конструкторы всех предков, начиная со старшего, которые инициализируют, а возможно перезаписывают значения полей данного объекта. И в конце конструктор самого объекта-наследника инициализирует свои новые поля или, возможно, перезаписывает значения полей, которые уже инициализировали конструкторы предков. Вопрос в следующем. Если у предка есть приватные поля и публичные геттеры к ним, а наследник не переопределяет эти поля и геттеры, то из объекта наследника можно, вызвав унаследованный геттер, получить значение поля из приватного поля предка. Как происходит в действительности? При создании объекта создаются объекты всех его предков в отдельных областях памяти, со всеми своими полями? Или же, что мне кажется более вероятным, JVM понимает, что при наличии публичных геттеров у предка наследник может получить доступ к его приватным полям и поэтому создаёт в области памяти объекта-наследника приватные поля его предка?


Ответ

Объектный модуль класса, помимо прочей информации, содержит информацию о иерархии наследования, порядке следования полей и их размерах. То есть после загрузки класса у виртуальной машины в метаспейсе всегда есть "карта" объектов этого класса, по которой можно вычислить по какому смещению от начала блока памяти находится то или иное поле. При создании нового объекта выделяется блок достаточного объёма, чтобы хранить заголовок объекта, его поля, а также поля всех его суперклассов. Причём поля располагаются в порядке от корня наследования - сначала поля суперкласса, потом подкласса. Благодаря такому расположению "карта" суперкласса подходит для ориентирования в объекте подкласса. Для наглядности определим примитивную иерархию классов
class A { int x; int y; }
class B extends A { int z; }
Операция B obj = new B() выделит в куче такой блок
----------- -- | Заголовок | | ----------- |_ Класс A | x | | | y | | ----------- -- | z | |- Класс B ----------- --
Если мы теперь приведём тип объекта к базовому, то виртуальная машина будет выполнять операции доступа к полям объекта так, будто никакого хвостика, содержащего поле z, просто не существует.
Благодаря Алексею Шипилёву мы можем увидеть это вживую с помощью инструмента jol (Java Object Layout).
import org.openjdk.jol.info.ClassLayout; import org.openjdk.jol.vm.VM;
public class ShowLayout { public static void main(String[] args) throws Exception { System.out.println(VM.current().details()); System.out.println(ClassLayout.parseClass(B.class).toPrintable()); } }
Компилируем
javac -cp jol-cli-0.9-full.jar ShowLayout.java
Запускаем
java -javaagent:jol-cli-0.9-full.jar ShowLayout
Получаем
# Running 64-bit HotSpot VM. # Using compressed oop with 3-bit shift. # Using compressed klass with 3-bit shift. # Objects are 8 bytes aligned. # Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] # Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
B object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 12 (object header) N/A 12 4 int A.x N/A 16 4 int A.y N/A 20 4 int B.z N/A Instance size: 24 bytes Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Для методов каждого класса у JVM тоже есть "карта", указывающая по какому смещению в памяти находится начало того или иного метода. Есть два способа использования этой "карты" - раннее и позднее связывание (на самом деле больше, но это несущественно в текущем контексте). Обычный вызов метода
obj.getZ()
будет скомпилирован в байткод
aload_1 // Загрузка в стек ссылки на obj invokevirtual #4 // Method B.getZ:()I
Инструкция invokevirtual использует позднее связывание. То есть при вызове метода JVM анализирует контекст вызова (call site), определяет какой именно метод нужен и передаёт управление по требуемому смещению. Благодаря этому и возможен полиморфизм.
Вызов метода суперкласса (а также вызовы конструкторов и приватных методов)
super.getX();
будет скомпилирован в байткод
aload_0 // Ссылка this на объект класса B invokespecial #2 // Method A.getX:()I
Инструкция invokespecial использует ранее связывание. То есть ещё на этапе загрузки класса понятно какой именно метод какого именно класса надо будет вызвать, и JVM "зашивает" смещение этого метода в байткод.
Получив управлением метод getX (с помощью JVM, конечно) отсчитает нужное смещение от начала блока памяти, на который указывает переданная ссылка, до места где должно располагаться поле x в классе A, прочитает его значение, положит на вершину стека и вернёт управление вызывающему коду. Даже не догадываясь, что ссылка this на самом деле указывает на объект большего размера, и есть ли в подклассе метод с таким же именем, как у него.

Как корректно записать/считать в/из файла структуру с полями типа string?

Имеется структура
struct User { string login; string password; };
Стоит задача сделать примитивную авторизация пользователя. Т.е. создается файлик, в него записывается заполненный объект вышеуказанной структуры, а при последующих запусках производится запрос логина+пароля, считываются данные из файлика и сравниваются. Код:
#include #include #include #include #include
using namespace std;
struct User { string login; string password; };
void main () { SetConsoleCP (1251); // установка универсальной кодировки SetConsoleOutputCP (1251);
string path; int realsize=0; User u; vector U; bool exit = false;
do { system("cls"); cout<<"Укажите, на каком диске находится файл с регистрационными данными:
"; getline(cin, path); path += ":\\users.txt"; ifstream fin(path, ios_base::binary | ios_base::in);
if (fin.is_open()) { cout<<"Отлично, ваш файл найден!
"; fin.read((char*)&u, sizeof(User)); U.push_back(u); fin.close(); cout << "Введите логин:
"; getline(cin, u.login); cout << "Введите пароль:
"; getline(cin, u.password); if (!U.at(0).login.compare(u.login) && !U.at(0).password.compare(u.password)) { cout << "Вы авторизованы!
"; } else { cout << "Вы не авторизованы!
"; } exit = true; } else { cout << "Файл не найден и будет создан"; ofstream fout (path, ios_base::binary | ios_base::out); if (fout.is_open()) { cout << "Введите логин:
"; getline(cin, u.login); cout << "Введите пароль:
"; getline(cin, u.password); fout.write((char*)&u, sizeof(User)); fout.close(); cout << "Файл создан и данные внесены!
"; } else { cout << "Ошибка при создании файла! Работа приложения будет завершена.
"; exit = true; } } } while(!exit);
system("pause");
}
Вся беда в том, что при выполнении данного кода появляется ошибка:
Необработанное исключение по адресу 0x0FDECCC8 (msvcp110.dll) в test.exe: 0xC0000005: нарушение прав доступа при чтении по адресу 0x0067ADE4.
Кадры стека вызовов:

Путем экспериментов установлено, что замена использования вектора на простой динамический массив никак не влияет на ошибку (т.е. она по-прежнему появляется), а вот замена использования string на char * приводит к устранению проявления данной ошибки. Погуглив, я пришел в выводу, что возникает какая-то ошибка в деструкторе string (могу ошибаться). Может кто-нибудь прояснить ситуацию и дать рекомендации по корректному использованию типа string в данного рода задачах?


Ответ

Самый простой способ (как уже упомянул в комментарии @pavel) - использовать текстовый режим работы с файлом и операторы форматированного ввода/вывода (operator<<, operator>>) для чтения/записи std::string из/в потока.
Чтение:
ifstream fin(path); if (fin) { fin >> u.login >> u.password; }
Запись:
ofstream fout(path); if (fout) { // Разделители нужны для последующего считывания fout << u.login << " " << u.password << "
"; }
При этом данный подход накладывает некоторые ограничения на строки: как минимум они не должны содержать в себе символы пробельной группы, т.к. такой символ будет расценен как разделитель.
Функции istream::read, ostream::write в этом случае не используются вовсе. Причина, по которой они не работают как надо указана в ответе @gbg.

Как сделать плавную прокрутку страницы?

Как делается плавная прокрутка страницы на сайте? Мы скроллим страницу, а она прокручивается не рывками, а плавно, как тут. Какие плагины для этого есть?


Ответ

Нашел для этих целей плагин jQuery.scrollSpeed

Стоит ли заменять анонимную реализацию с одним методом лямбдой?

Intllij Idea предлагает заменить анонимную реализацию с одним методом лямбдой. Но по смыслу это не функциональный интерфейс, а просто переопределение одного метода одного класса. Стоит ли заменять?
return new NextImportIdParams() { @Override public String getCollectionName() { return new CollectionName(); } };


Ответ

Все зависит от того под какую версию java пишете + от принятого code style. Если вам никто не связывает руки, то определенно лямбы делают код более компактным и читабельным.

Запуск ssh-agent на debian

На удаленном сервере для развертывания кода с BitBucket используем защишенное соединение иаутентификацию по ssh ключам. Ксамому серверу подключаюсь через putty (с win10 сижу), и каждый раз приходится заново запускать ssh-agent командой eval $(ssh-agent) и добавлять нужные ключи. Посмотрел в запущенные процессы, все ранее запущенные ssh-agent'ы висят в памяти, но если разорвать соединение с сервером и затем снова открыть, ssh-agent снова недоступен. Подскажи в чем может быть проблема?
devloop@s05657c43:~# ssh-agent SSH_AUTH_SOCK=/tmp/ssh-m8EJNB3MFimr/agent.13061; export SSH_AUTH_SOCK; SSH_AGENT_PID=13062; export SSH_AGENT_PID; echo Agent pid 13062;
devloop@s05657c43:~$ ssh-add .ssh/devloop Could not open a connection to your authentication agent.


Ответ

сам факт запуска процесса ssh-agent на отдалённой машине уже намекает на то, что вы, вероятно, используете его не по назначению.
цитата из man ssh-agent
The idea is that the agent is run in the user's local PC, laptop, or terminal. Authentication data need not be stored on any other machine, and authentication passphrases never go over the network.
мой вольный перевод:
идея в том, что ssh-agent запускается на локальном компьютере. чтобы никакие аутентификационные данные не нужно было хранить на другой машине, и никакие пароли не передавались по сети.

судя по этому фрагменту:
$ ssh-add .ssh/devloop
вы хотите с первой отдалённой машины аутентифицироваться на каких-то других отдалённых машинах по ключу, хранящемуся на удалённой машине (в файле ~/.ssh/devloop).
в таком случае вам нет смысла запускать на этой отдалённой машине процесс ssh-agent. для того, чтобы аутентифицироваться на других машинах с помощью этого ключа, можете воспользоваться любым из перечисленных вариантов:
переименуйте файл ~/.ssh/devloop в ~/.ssh/id_rsa. этот вариант не подойдёт, если такой файл уже есть и его содержимое требуется вам для каких-то других целей. указывайте явно этот файл при подключении:
$ ssh -i ~/.ssh/devloop другая-отдалённая-машина добавьте в начало файла ~/.ssh/config строку:
identityfile ~/.ssh/devloop
тогда программа ssh будет пытаться использовать и этот файл при аутентификации.
если этот файл требуется использовать только для аутентификации лишь на нескольких определённых машинах, тогда лучше вместо указанной строки добавить в конец файла ~/.ssh/config секцию:
host машина1 машина2 машина3 identityfile ~/.ssh/devloop

но если я неправильно угадал, и вам на самом деле необходим процесс ssh-agent, то лучше запускать его в качестве «прослойки» (как он собственно, и используется в современных дистрибутивах), т.е. сразу указывать при подключении команду вида ssh-agent оболочка, примерно так:
$ ssh -t отдалённая-машина ssh-agent bash
опция -t здесь необходима для того, чтобы был отведён tty (если не указывать команду, т.е., инициировать запуск оболочки, то tty отводится автоматически).
а ещё, пожалуй, лучше будет использовать терминальный мультиплексор (screen, tmux и т.п.), который, кстати, тоже можно запускать с «прослойкой» в виде ssh-agent, примерно так:
$ ssh -t отдалённая-машина ssh-agent screen

p.s. и, да, вопрос не несёт в себе никакой дистрибутивной специфики, а касается лишь конкретно клиента openssh

Как прослушивать нажатия клавиш клавиатуры вне приложения?

У меня есть задача: прослушивать нажатия клавиш клавиатуры, вне приложения (в браузере, блокноте или просто на рабочем столе). При помощи чего это можно реализовать? Не нашел никакой информации по этому поводу.


Ответ

Можно использовать готовую библиотеку. Например jkeymaster или jnativehook

Выполняется старая версия кода

Установил Visual Studio 2015. Создал проект. написал простую программу из разряда "привет мир". В какой то момент перестает компилироваться программа, то есть меняю реплику на "мир привет", а выдается "привет мир". То есть старый exe файл запускается. Кто подскажет как побороть?


Ответ

Причина запуска старого варианта кода может быть в том, что новый код содержит ошибки и согласно настройкам среды производится запуск последней удачной сборки.
Проверьте настройки на странице Tools - Options - Projects & Solutions - Build & Run