You can use Open3d for this:
import open3d as o3d
pcd = o3d.io.read_point_cloud("input.txt", format="xyz")
o3d.io.write_point_cloud("output.ply", pcd)
This will not preserve the intensity values, though.
If you need them use open3d.t:
pcd = o3d.t.io.read_point_cloud("input.txt", format="xyzi")
o3d.t.io.write_point_cloud("output.ply", pcd)
Note that PLY comes in two flavours: ASCII and binary. The above solution gives you a binary file, which is the default for open3d. If you really need ASCII then use this:
o3d.t.io.write_point_cloud("output.ply", pcd, write_ascii=True)
Alternatively, since ASCII is basically plain text, you could do this without any external library by just replacing the first line of your .txt file with a custom header so it looks like this:
ply
format ascii 1.0
element vertex {insert number of points here}
property float x
property float y
property float z
property float scalar_Intensity
end_header
point1_x point1_y point1_z point1_intensity
point2_x point2_y point2_z point2_intensity
...
pointN_x pointN_y pointN_z pointN_intensity
and saving it with a .ply extension.
Lastly, if you just need a "tool" then check out CloudCompare, which handles many file format conversions.