@רק-טוב מה שעשית זה מעולה, רק שבתור המלצה יהיה תמיד עדיף להשתמש ב-PHPMailer מאשר להשתמש ב-mail.

@רק-טוב מה שעשית זה מעולה, רק שבתור המלצה יהיה תמיד עדיף להשתמש ב-PHPMailer מאשר להשתמש ב-mail.

בסופו של דבר השתמשתי ב-session, ונעזרתי בפוסט:
https://f2.freeivr.co.il/topic/13141/מה-זה-סשן-ואיך-משתמשים-בו/2
@צדיק-תמים כתב בהוספת משתנים באופן יזום לבקשה:
session_id($_GET['ApiCallId']);
תודה רבה,
זה עזר לי מאוד!!
@אA
ההגדרה הזו מסירה את ההודעה של: "שלוחה 15" בכניסה לשלוחה.
עדיף להשתמש ב-PHPMailer.
קודם כל יש להוריד את החבילה באמצעות composer:
composer require phpmailer/phpmailer
וזה קוד לדוגמא:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php'; // Include Composer's autoloader
$mail = new PHPMailer(true);
try {
// SMTP Configuration
$mail->isSMTP();
$mail->Host = 'smtp.example.com'; // Replace with your SMTP host
$mail->SMTPAuth = true;
$mail->Username = 'your_email@example.com'; // Your email
$mail->Password = 'your_email_password'; // Your email password
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; // Use `PHPMailer::ENCRYPTION_SMTPS` for SSL
$mail->Port = 587; // Change to 465 if using SSL
// Sender & Recipient
$mail->setFrom('your_email@example.com', 'Your Name');
$mail->addAddress('recipient@example.com', 'Recipient Name');
// Attachments
$mail->addAttachment('/path/to/file.pdf'); // Example: Attach a PDF
$mail->addAttachment('/path/to/image.jpg', 'CustomFileName.jpg'); // Rename attachment
// Email Content
$mail->isHTML(true);
$mail->Subject = 'Test Email with Attachment';
$mail->Body = '<h3>Hello,</h3><p>This is a test email with an attachment.</p>';
$mail->AltBody = 'Hello, This is a test email with an attachment.'; // Plain text fallback
// Send Email
$mail->send();
echo 'Email sent successfully!';
} catch (Exception $e) {
echo "Email could not be sent. Error: {$mail->ErrorInfo}";
}
?>
@אופיר מסכים ב-100%.
היו צריכים לדעתי להפריד את זה ל-2 הגדרות שונות.
@צדיק-תמים אני בדקתי, ומה ש@עידו אומר זה אמת.
יש בעיה בסיריאליזציה ב-C#, אגב, בדקתי את זה ב-postman וזה עובד (עם בקשת POST), ובדקתי את זה גם ב-PHP וזה גם עובד (גם עם בקשת POST).
אם יש לך השגות על מה שנאמר כאן, אני מזמין אותך לנסות בעצמך את הנ"ל.
@עידו בניתי גם כן קוד ב-C# וקיבלתי את אותה השגיאה כאשר השתמשתי ב- HttpClient.PostAsync, כנראה שיש בעיה בסיראליזציה של ה-body, והטוקן לא מצליח להיות מתורגם כראוי.
למרות שנהוג להשתמש ב-POST לצורך יצירת משאבים (קבצים, נתנוים וכד'), כאן השתמשתי ב-HttpClient.GetAsync לצורך עקיפת הבעיה, וזה אכן הצליח ליצור קובץ לשלוחה הרצויה.
אגב, לצורך שימוש ב-API צריך לבצע 2 בקשות:
להלן הקוד:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace YemotUploadTextFile
{
internal class Program
{
static void Main(string[] args)
{
UploadTextFileWithGet().GetAwaiter().GetResult();
Console.WriteLine("Done!");
}
static async Task UploadTextFileWithGet()
{
var username = "<username>";
var password = "<password>";
var client = new HttpClient();
try
{
// login yemot system for getting token
HttpResponseMessage responseJ = await client.GetAsync(
$"https://www.call2all.co.il/ym/api/Login?username={username}&password={password}");
// בדיקת מצב התגובה
if (responseJ.IsSuccessStatusCode)
{
// קריאת התוכן של התגובה
string responseContent = await responseJ.Content.ReadAsStringAsync();
Console.WriteLine($"Response: {responseContent}");
var responseObj = JsonConvert.DeserializeObject<JObject>(responseContent);
if (responseObj["responseStatus"].ToString() == "OK")
{
// send a get request to upload file on yemot server
string token = responseObj["token"].ToString();
string what = "ivr2:14/text_file.ini";
string contents = "Some test message";
Console.WriteLine($"Response: {token}");
client = new HttpClient();
var responseJ2 = await client.GetAsync($"https://www.call2all.co.il/ym/api/UploadTextFile?token={token}&what={what}&contents={contents}");
// בדיקת מצב התגובה
if (responseJ2.IsSuccessStatusCode)
{
// קריאת התוכן של התגובה
var responseContent2 = await responseJ2.Content.ReadAsStringAsync();
Console.WriteLine($"Response: {responseContent2}");
}
else
{
Console.WriteLine($"Error: {responseJ2.StatusCode}");
return;
}
}
else
{
Console.WriteLine($"Error: {responseObj["message"]}");
return;
}
}
else
{
Console.WriteLine($"Error: {responseJ.StatusCode}");
return;
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
await Task.Delay(1000);
Console.WriteLine("Async work done!");
}
}
}
@עידו הרצתי עכשיו דרך ה-bash סידרה של שליחת בקשות CURL של POST, ואכן שבשביל שזה יעבוד, צריך להוריד את ה-flag של -d, ובמקום זה לשים flags של:
\--header "Content-Type: application/json" \--data-raw '{"token":"<username>:<password>","what":"ivr2:14/text_file.ini","contents":"Some test message"}'
לגבי זה שזה עובד במקרים אחרים, אני לא יודע מה לומר לך. מה שכן, בשביל שזה יעבוד בפורמט JSON אתה תצטרך ככל הנראה לבנות CURL באופן ידני, כי ה-httpClient.PostAsync לא בונה נכון את ה-CURL.
בכל אופן, הצעתי לך אתמול פתרון שכן עובד, תשתמש בו.
@עידו החלטתי להגדיל ראש ובדקתי את העניין ע"י שליחת בקשת POST, בפורמט של x-url-encoded (במקום בפורמט של application/json), וזה עבד.
כנראה באמת יש בעיה בסיריאליזציה בפורמט JSON.
להלן הקוד:
static async Task UploadTextFileWithPost()
{
try
{
var client = new HttpClient();
// Prepare key-value pairs
var formData = new Dictionary<string, string>
{
{ "token", $"{Username}:{Password}" },
{ "what", "ivr2:14/text_file.ini" },
{ "contents", "Some test message" }
};
// Create FormUrlEncodedContent
var content = new FormUrlEncodedContent(formData);
// Optional: Add headers (Content-Type is set automatically)
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
var responseJ2 = await client.PostAsync("https://www.call2all.co.il/ym/api/UploadTextFile", content);
// בדיקת מצב התגובה
if (responseJ2.IsSuccessStatusCode)
{
// קריאת התוכן של התגובה
var responseContent2 = await responseJ2.Content.ReadAsStringAsync();
Console.WriteLine($"Response: {responseContent2}");
}
else
{
Console.WriteLine($"Error: {responseJ2.StatusCode}");
return;
}
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
await Task.Delay(1000);
Console.WriteLine("Async work done!");
}
@אA לגבי @מנחם, כתוב בפרופיל שלו:
"הצטרף ב- 18 במאי 2020, 16:11 התחבר לאחרונה 16 בנוב׳ 2022, 21:58",
הוא התחבר לאחרונה לפני שנתיים+, תנסה אולי מנהל פעיל כמו @eliyahu
@Freund להלן קוד שעובד להעלאת קובץ:
<?php
$url = "https://www.call2all.co.il/ym/api/UploadFile";
// Prepare multipart form data
$data = [
"token" => "$username:$password",
"path" => "ivr2:14/test.wav",
"file" => new CURLFile("audio/test.wav", "audio/wav", "test.wav") // File upload
];
// Make the request
$response = curl_post_request($url, $data);
echo "Response: " . $response;
# POST request helper function
function curl_post_request(string $url, $data, array $headers = [])
{
// Initialize cURL
$ch = curl_init();
// Set cURL options
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data); // Multipart data
// Set headers (DO NOT manually set Content-Type for multipart)
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// Execute request
$response = curl_exec($ch);
// Handle errors
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
} else {
return $response;
}
}
@אאד קוד להמחשה עבור שלוחת API (לא עשיתי על זה debugging), אבל העקרון מומחש היטב.
הקוד בודק בקובץ האם מס' הת.ז קיים באחת מהרשומות בקובץ המקור, במידה וכן, הקוד עובר על כל אחד משדות של הרשומה התואמת ושואל את המשתמש האם לעדכן. במידה ולא, הפרטים הרלוונטים נלקחים מהמשתמש, ורשומה חדשה מוזנת לקובץ המקור.
להלן קובץ dataSource בפורמט CSV: (ניתן להמיר את זה בקובץ מקור ב-EXCEL, ולממש את זה בהתאם ע"י שימוש בחבילת: phpoffice/phpspreadsheet, תוריד אותה לפרויקט שלך באמצעות ה-composer)
datasource.csv
<?php
$apiCallId = $_GET['ApiCallId'];
$apiPhone = $_GET['ApiPhone'];
if (!$_GET['identity']) {
echo 'read=t=אנא הזן את מספר תעודת הזהות=identity,,9,,15,Digits,';
exit;
}
$csvFile = __DIR__ . '/./datasource.csv'; // קובץ מקור
$tempFile = __DIR__ . '/./temp.csv'; // קובץ זמני לשמירה
$dataSource = [];
$matchRecord = [];
$matchIndex = null;
// קריאת קובץ CSV המכיל את כל נתוני המשתמשים
if (($handle = fopen($csvFile, "r")) !== false) {
$headersRow = fgetcsv($handle); // דילוג על השורה הראשונה שמכילה כותרות לעמודות
$dataSource[] = $headersRow;
$index = 1;
while (($row = fgetcsv($handle, 1000, ",")) !== false) {
$dataSource[] = $row; // הוספת כל שורה למערך
if (trim($row[1]) === trim($_GET['identity'])) {
$matchRecord = $row;
$matchIndex = $index;
break;
}
$index++;
}
fclose($handle);
} else {
echo "שגיאה בפתיחת קובץ ה-CSV!";
}
if ($matchRecord) { // מצב עדכון משתמש לאחר מציאת התאמה עם רשומה כלשי בקובץ מקור CSV
if ($matchRecord[2]) { // בדיקה האם שם המשתמש הוזן לרשומה
if (!$_GET['name_confirmation']) {
$name = trim($matchRecord[2]);
echo "read=t=שמכם כפי שמופיע הוא $name, לאישור הקש 1, לתיקון הקש 2=name_confirmation,,1,1,8,Digits,";
exit;
}
// במידה ומקש 2 לתיקון השם הוקש
if ($_GET['name_confirmation'] == 2) {
if (!$_GET['new_name']) {
// הזנת ההקלטה + תמלול - מודול זה כרוך בתשלום של 0.4 יחידות
echo "read=t=אנא הקלט את שמך=new_name,,voice,,no,,record,";
exit;
}
}
# וכן על זה הדרך עבור כל השדות שאתה רוצה לבדוק
# .....
} else { // השם לא הוזן לרשומה
if (!$_GET['new_name']) {
echo "read=t=אנא הקלט את שמך=new_name,,voice,,no,,record,";
exit;
}
}
$phone = $matchRecord[0];
$identity = $matchRecord[1];
$name = $_GET['name_confirmation'] == 2 ? trim($_GET['new_name']) : trim($matchRecord[2]);
# וכן ניתן להוסיף עוד שדות
#....
$updateRecord = [$phone, $identity, $name];
for ($i = 0; $i < $dataSource[$matchIndex]; $i++) {
$dataSource[$matchIndex][$i] = $updateRecord[$i];
}
} else { // מצב יצירה של משתמש, כאשר אין התאמה של ת.ז עם אף רשומה בקובץ מקור
if (!$_GET['name']) {
echo "read=t=אנא הקלט את שמך=new_name,,voice,,no,,record,";
exit;
}
$phone = $apiPhone;
$identity = trim($_GET['identity']);
$name = trim($_GET['name']);
# וכן ניתן להוסיף עוד שדות
#....
// יצירת מערך שיהא רשומה חדשה במסמך CSV
$newRecord = [$phone, $identity, $name];
$dataSource[] = $newRecord;
}
$output = fopen($tempFile, "w"); // פתיחת קובץ זמני לכתיבה
foreach ($dataSource as $record) {
fputcsv($output, $record); // כתיבת השורה המעודכנת
}
fclose($output);
// מחיקת הקובץ הישן והחלפתו בקובץ החדש
unlink($csvFile);
rename($tempFile, $csvFile);
@עידו אוקיי הבנתי מה עשית. יכול להיות שהבעיה היא הנקודה בסוף - אתה צריך להוריד אותה כי היא לא משוייכת לשום t. תשתמש ב-substr.
@עידו באמצעות שלוחת API באופן הבא:
@עידו כשאני משתמש ב-read עם פרמטר record, אז מה שמוחזר בבקשה העוקבת זה הניתוב לקובץ שהוקלט.
ועם הניתוב הזה אתה עושה DownloadFile.
שם ההקלטה הוא לא קבוע מראש, אלא כחלק מהפרמטרים שאתה מזין ב-read (עבור record), כלומר שם התיקיה ושם הקובץ שבהם אתה שומר את ההקלטה בימות המשיח, וזה שרירותי, כלומר אתה בוחר את הפרמטרים הללו.
לדוגמא: (קוד להמחשה שבניתי)
if (!$_GET["target_path_of_name"]) {
readRecord("t-אנא הקליטו את שמכם בקול ברור, לסיום הקישו סולמית", "target_path_of_name", "", "/6/1", time() . "_caller_name_record");
exit;
}
$target_path_of_name= $_GET["target_path_of_name"];
$audio_stream = post({
url: 'https://www.call2all.co.il/ym/api/DownloadFile',
token: 'username:password',
path: "ivr2:$target_path_of_name"
})
$destPath = __DIR__ . '/./audio/' . time() . 'bla.wav';
file_put_contents($destPath, $audio_stream);
function readRecord(
$prompt,
$param_name,
$get_new_value = '',
$api_dir = '',
$file_name = '',
$play_menu = '',
$save_on_hangup = '',
$file_exists_record_append = '',
$min_record_time = '',
$max_record_time = ''
) {
echo "read=$prompt=$param_name,$get_new_value,record,$api_dir,$file_name,$play_menu,$save_on_hangup,$file_exists_record_append,$min_record_time,$max_record_time";
}